1. 程式人生 > >C語言中scanf與分隔符(空格回車Tab)

C語言中scanf與分隔符(空格回車Tab)

眾所周知,C語言中的scanf函式的作用是從標準輸入裝置(通常是鍵盤)讀取輸入值,並存儲到引數列表中指標所指向的記憶體單元。下面從幾個方面說一下一些稍微細節的東西。下面的實驗都在vc6.0中通過。

1、scanf的返回值

scanf通常返回的是成功賦值(從標準輸入裝置賦值到引數列表所指定的記憶體區域)的資料項數,如果出錯或是遇到end of file(注意,如果想從鍵盤輸入EOF,在windows的DOS視窗用Ctrl+Z 或F6;在UNIX系統上,用CTRL+D。),則返回EOF,比如:

scanf("%d%d", &x, &y);

如果x和y都被成功讀入,那麼scanf的返回值就是2;
如果只有x被成功讀入,返回值為1;
如果x和y都未被成功讀入,返回值為0;
如果遇到錯誤或遇到end of file,返回值為EOF。

2、scanf的處理機制

scanf以刪除的方式從緩衝區讀入資料(來自標準輸入裝置的資料儲存在緩衝區),也就是說,scanf從緩衝區讀入一個數據項,該資料項在緩衝區中就被清除掉了。而如果scanf需要讀取一個數據項,返現緩衝區當前是空的,那麼程式就會在scanf程式碼處阻塞,等待使用者輸入,scanf函式接收到相應的資料項之後,在緩衝區中將這一資料項清除,scanf函式返回,程式繼續執行。

3、scanf對不同型別輸入的處理方式

首先,要清除一個概念:空白字元(white space)。一般,程式中所指的空白字元是指空格(space),回車(enter)和指標符(table)。

3.1 整數%d

對於整型資料的輸入,也就是說"%d"型別的輸入,scanf預設的分割符是所有的空白字元(空格,回車和指標符都行)。也就是說如果一個scanf函式中出現scanf("%d%d",&a,&b),那麼用任何一個空白字元來分隔兩個整數a,b的值,變數a,b都可以接收到正確的輸入。另外,要注意的是,scanf對於數字輸入,會忽略輸入資料項前面的空白字元。下面是例1:

  1. Code:  
  2. #include<stdio.h>
  3. int main()  
  4. {  
  5.     int a,b;  
  6.     printf("Input the value of a and b:");  
  7.     while(scanf("%d%d",&a,&b)!=EOF)  
  8.     {  
  9.         printf("a=%d,b=%d\n",a,b);  
  10.         printf("Input the value of a and b:");  
  11.     }  
  12.     return 0;  
  13. }  
  14. Output:  
  15. Input the value of a and b:123 456  
  16. a=123,b=456  
  17. Input the value of a and b:123  456  
  18. a=123,b=456  
  19. Input the value of a and b:123  
  20. 456  
  21. a=123,b=456  
  22. Input the value of a and b:  
  23. 123 456  
  24. a=123,b=456  
  25. Input the value of a and b:     123 456  
  26. a=123,b=456  
  27. Input the value of a and b: 123 456  
  28. a=123,b=456  
  29. Input the value of a and b:^Z  
  30. Press any key to continue
3.2 字串%s

scanf對於字串輸入的處理和對整數類似,會忽略前導的空白字元,而且預設的分隔符是所有的空白字元。但是,要注意的是,由於C語言中,沒有string型別,都是用char型陣列來表示。因此,scanf會為每一個輸入的字串最後加一個‘\0’。下面是一個例子,可以看出scanf這貨的邊界控制還是要小心。如下例2。

  1. #include<stdio.h>
  2. int main()  
  3. {  
  4.     char a[5],b[5];  
  5.     int i;  
  6.     printf("Input the value of a and b:");  
  7.     while(scanf("%s%s",a,b)!=EOF)  
  8.     {  
  9.         printf("a=%s,b=%s\n",a,b);  
  10.         for(i=0;i<5;i++)  
  11.             printf("%d:(%c) ",a[i],a[i]);  
  12.         printf("\n");  
  13.         for(i=0;i<5;i++)  
  14.             printf("%d:(%c) ",b[i],b[i]);  
  15.         printf("\n");  
  16.         printf("Input the value of a and b:");  
  17.     }  
  18.     return 0;  
  19. }  
執行結果:


3.3 字元%c

scanf在處理對字元資料的輸入時,既不會忽略前導空白字元,預設也沒有任何分隔字元。所有的字元,包括空白字元都會被當成輸入字元。下面是例3。

  1. #include<stdio.h>
  2. int main()  
  3. {  
  4.     char a ,b ;  
  5.     printf("Input the value of a and b:");  
  6.     while(scanf("%c%c",&a,&b)!=EOF)  
  7.     {  
  8.         printf("a=%c,b=%c\n",a,b);  
  9.         printf("Input the value of a and b:");  
  10.     }  
  11.     return 0;  
  12. }  
執行結果:


可以看出,在對字元資料輸入的時候,由於緩衝區中有回車空格等資料,會導致輸入資料比較詭異,為了解決這個問題,有以下方法:

(1) 清空緩衝區

在微軟系統中,有一個名為fflush(stdin)的函式,可以用來清空緩衝區,如下例4。

  1. #include<stdio.h>
  2. int main()  
  3. {  
  4.     char a ,b ;  
  5.     printf("Input the value of a and b:");  
  6.     while(scanf("%c%c",&a,&b)!=EOF)  
  7.     {  
  8.         printf("a=%c,b=%c\n",a,b);  
  9.         fflush(stdin);  
  10.         printf("Input the value of a and b:");  
  11.     }  
  12.     return 0;  
  13. }  
執行結果:


(2)將緩衝區的資料讀出來

有的編譯系統並沒有定義stdin的fflush操作,這個時候,可以把緩衝區中的資料讀出來,有如下幾種可行的方法:

1) getchar()

將例4中的fflush(stdin);語句換成

char c;
while((c=getchar())!='\n'&&c!=EOF);

執行效果和上面的相同。

2)gets()

char* gets(char* buffer)從stdin流中讀取字串,直至接受到換行符或EOF時停止,並將讀取的結果存放在buffer指標所指向的字元陣列中。換行符不作為讀取串的內容,讀取的換行符被轉換為null值,並由此來結束字串。讀入成功,返回與引數buffer相同的指標;讀入過程中遇到EOF(End-of-File)或發生錯誤,返回NULL指標。所以在遇到返回值為NULL的情況,要用ferror或feof函式檢查是發生錯誤還是遇到EOF。
要注意的是gets函式可以無限讀取,不會判斷上限,所以應該確保buffer的空間足夠大,以便在執行讀操作時不發生溢位。如果溢位,多出來的字元將被寫入到堆疊中,這就覆蓋了堆疊原先的內容,破壞一個或多個不相關變數的值。

將例4中的fflush(stdin);語句換成

char c[10];
gets(c);

執行效果也和上面的相同。

  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. #include<string.h>
  4. char *method1(void)  
  5. {  
  6.     staticchar a[4];  
  7.     scanf ("%s\n", a);  
  8.     return a;  
  9. }  
  10. int main(void)  
  11. {  
  12.     char *h = method1();  
  13.     printf ("%s\n", h);  
  14.     return 0;  
  15. }  
執行結果:
  1. ab  
  2. cd  
  3. ab  
  4. Press any key to continue
可以發現,輸如兩次之後才會輸出。這個現象比較詭異,原因如下:

White space (such as blanks, tabs, or newlines) in the format string match any amount of white space, including none, in the input.  Everything else matches only itself.
Thus with scanf ("%s\n", a) it will scan for a string followed by optional white space. Since after the first newline more whitespace may follow, scanf is not done after the first newline and looks what's next. You will notice that you can enter any number of newlines (or tabs or spaces) and scanf will still wait for more.
However, when you enter the second string, the sequence of whitespace is delimited and scanning stops.