1. 程式人生 > >C 中scanf ( ) 函式用法 用法

C 中scanf ( ) 函式用法 用法

我覺得,在輸入輸出函式中,scanf()函式,應該是最麻煩的,有時它給我們的結果很可笑,但是一定是一原因的....

首先宣告一下,這篇日誌不是介紹scanf()中各種格式符用法的文章(沒有這個必要,但是大家一定要會用).

我嘗試了很多種輸入,包括一些錯誤的練習,曾經對scanf()由迷茫轉向清醒,又由清醒再次轉向迷茫......不知道何時是個盡頭,誰讓C如此高深呢?

在這裡貼出來,也是想讓自己時而不時能看到,也想知道自己的理解是否有錯,錯在哪裡(所以我就厚著臉皮,放在上面了).

注意 , 鍵盤緩衝區 與輸入有著密切的關係 ,並且, 型別匹配 對 輸入也極為重要!!

下面進入主題:

scanf對流的操作遵從型別匹配操作原則,如果型別不匹配,它將不讀取輸入流。 因此輸入流將滯留,如果輸入流不空,scanf不會等待使用者輸入,直接從緩衝區中輸入.

 
但是,scanf() 怎樣匹配? stdin又是什麼? 
在網上搜到的關於匹配的非常少,有些細節原因還是找不到.
所以,我自作主張的下了點結論:
例: scanf("%d,%d",&i,&j);  輸入:12 ,13回車 但是,j!=13. //注意,12後有一個空格,why?   
    原因:我解釋為,在scanf()中,格式字串中普通字元(不包括空白字元)實行的是 嚴格匹配,因為格式串中%d後面是一個 ','  ,因此輸入中數字12後必須為一個','.

      scanf("1123%s",&str);  輸入:1123aaabb 時str為 aaabb,但是,輸入 24aabbdd時,   會出錯,因為1123必須進行嚴格匹配.

另外:  scanf("%d/n",&i); printf("i=%d",i);      要怎麼輸入才能輸出: i=12    ? 它不是你想像中的那樣,有機會嘗試一下吧!疑問 
一些樣例:
scanf()是一個有返回值 的函式,它的返回值是什麼?怎麼樣利用這個特性?
scanf()中的匹配原則: 在本文 第五點 具體說明... 
scanf()中各種資料格式匹配的開始條件,結束條件 . 
         如: %d ,/n等型別輸入結束條件.

          scanf("%d/n",&i);printf("%d",i);  輸入 12回車,並無輸出,why? 
scanf()函式的結束條件: 

當各個 格式符 匹配完畢,且最後有一個回車時,函式結束.
scanf("%s",str)連續輸入127個就不能繼續輸入了.  //TC中,VC好像是4000多..

//說明鍵盤緩衝區長度為一個位元組嗎?但是  stdin->bsize(緩衝區大小)事實上為 512,這又是為什麼?
stdin緩衝區中的資料殘留 :  scanf("%3s",str); c= getchar();  輸入: aaabbccc回車,  此時str="aaa",c='b';  //緩衝區中資料殘留! 
getch()不經過緩衝區,直接接收鍵盤上輸入的字元. 
         //在上例中,加上一個 ch=getch();  但是getch()並不能讀取bbccc中的任何一個,說明 getch()與getchar()並不一樣,並且它們對Enter讀取的值也不同!
一個不常用的格式符:    %[]  ,如  scanf("%[a-z]",str);   

輸入: abcdefdsaABCDEF  輸出:str="abcdefdsa" ;
怎麼用scanf()來輸入一個有空格的字串?    疑問 
scanf()處理時,一個Enter送到緩衝區中有兩個值 :   一個回車(10) ,一個換行(13). 可以用 getchar()來接收(但是,在只能接收到/n,即13).
在一個scanf()函式之後加個fflush(stdin)可以清除輸入資料殘留?   
   scanf("%3s",str); fflush(stdin); c=getchar();

   直接輸入 aaabbbddd回車, c還能取得值嗎? 
下面是詳細解釋:
scanf()函式執行成功時的返回值是成功讀取的變數數 , 也就是說,你這個scanf()函式有幾個變數,如果scanf()函式全部正常讀取,它就返回幾。但這裡還要注意另一個問題,如果輸入了非法資料,鍵盤緩衝區就可能還個有殘餘資訊問題。
scanf()- 函式是通用終端格式化輸入函式,它從標準輸入裝置(鍵盤) 讀取輸入的資訊。可以讀入任何固有型別的資料並自動把數值變換成適當的機內格式。
sscanf() - 從一個字串中讀進與指定格式相符的資料.
函式原型:
Int  sscanf( string str, string fmt, mixed var1, mixed var2 ... );
int  scanf( const char *format [,argument]... );
sscanf與scanf類似,都是用於輸入的,只是後者以螢幕(stdin)為輸入源,前者以固定字串為輸入源。
其中的format可以是一個或多個 {%[*] [width] [{h | l | I64 | L}]type | ' ' | '/t' | '/n' | 非%符號}
如:  sscanf("123456", "%s", buf);  
     puts(buf);  //結果為: 123456
下面主要說一下scanf()的用法:
scanf函式的一般形式
scanf(格式控制,地址表列)
int scanf(char *format[,argument,...]);
“格式控制”的含義同printf函式;“地址表列”是由若干個地址組成的表列,可以是變數的地址,或字串首地址。
scanf()函式返回成功賦值的資料項數,出錯時則返回EOF。 
注:  scanf()中空白字元(包括/n,space)會使scanf()函式在讀操作中略去輸入中的零個或者一個或者多個空白字元,空白符可以是space,tab,換行 等等,直到第一個非空白符出現為止。//下一個格式符為%c也同樣如此. 如,scanf("%d %c",&i,&ch); 輸入:11             A回車,i=11,ch=A. 這裡ch並不為空格. 
     一個非空白字元會使scanf()函式在讀入時剔除掉與這個非空白字元相同的字元。
     如:  scanf("   %c",&ch);  輸入: 若干個回車後,輸入 A,     ch=A.  
          scanf("5729%s",str); 輸入:  5729okok              str=okok.  但是請注意:當輸入的前幾個不是5729時(就算以空格開始也不行!),將會出錯,str值不變.  
scanf()中 格式字元 說明
    %p 讀入一個指標 
    %[] 掃描字元集合  
    %n 至此已讀入值的等價字元數 
    %s 讀入一個字串,遇空格、製表符或換行符結束。
    %c   %d   %i   %o  %x   %X  %c   
%f 輸入實數,可以用小數形式或指數形式輸入。
%F   %e   %E   %g   %G  
%u 讀入一個無符號十進位制整數  
%% 讀%符號
附加格式說明字元表修飾符 說明
    L/l 長度修飾符 輸入"長"資料
h 長度修飾符 輸入"短"資料
W 整型常數 指定輸入資料所佔寬度
m指定輸入資料所佔的寬度
* 星號 空讀一個數據
結合實際例程,一一闡述:
一:   "%d%d%d"
         是按十進值格式輸入三個數值。輸入時,在兩個資料之間可以用一個或多個空格、tab鍵、回車鍵分隔。
       "%d,%d,%d" 
執行時按如下方式輸入三個值:3,4,5 ↙(輸入a,b,c的值)或者3,□4,□5 ↙(輸入a,b,c的值)3,□□□4,□5 ↙(輸入a,b,c的值) 
................

都是合法的,但是輸入3□,4□□,5 ↙ 出錯!!!! 

    //因為scanf()的格式串中普通字元實行完全匹配!
          %c 
       在用"%c"輸入時,空格和“轉義字元”均作為有效字元。

二:    scanf()函式以一個非空白字元(包括空格,跳格,回車)開始一個數據的輸入 ( %c 當然除外!,但是注意,gets()以任意字元為開始! ). 
       scanf()函式接收輸入資料時,遇以下情況結束一個數據的輸入:(不是結束該scanf函式,scanf函式僅在每一個數據域均有資料,並按回車後結束)。 
    ① 遇空格、“回車”、“跳格”鍵。
② 遇寬度結束。
③ 遇非法輸入。    //這裡很重要,如果有非法輸入,則結束這個資料的輸入,但是輸入的非法資料還在緩衝區中,你可以用對應的資料型別接收!也可以乾脆清除緩衝區.
                       //     例如:
                                  i=10;
                                  scanf("%s",str);
                                  scanf("%d",&i);
                                  scanf("%s",str2);
                                  printf("%s/n",str);printf("%d/n",i);printf("%s/n",str2);
                         輸入: i love!
                         輸出: i
                                10

                                love!

    //因為 love對 %d來說是一個開始輸入(scanf()以一個非空白開始),但是因為不合法,

      所以這個開始也就是結束,i值不變!                         
三 .   scanf()函式能不能正確接受有空格的字串?如: I love c! 

      //事實上它可以!!!
     char str[80];
     scanf("%s",str);          //輸入  I love c?   //結果: 輸出 I
        分析:  scanf()掃描到"I"後面的空格就認為對str的賦值結束,並忽略後面的"love c!" . 這裡要注意是"love  c!"還在鍵盤緩衝區
         經過除錯發現,其實這時緩衝區字串首尾指標已經相等了,也就是說緩衝區清空了,scanf()函式應該只是掃描stdin流,這個殘存資訊是在stdin中.

        //其實,我曾試過,用scanf("%s",str)連續輸入127個字元後,鍵盤緩衝區就裝不下了,也就是說,對輸入的不做處理,繼續輸入,就沒有反應了,只有輸入回車才有效.

   來驗證一下:                 
    #include <stdio.h>                                                                     
int main()
{
char str[80];
char str1[80];
char str2[80];
scanf("%s",str);          /*此處輸入:I love you! */
printf("%s",str);
sleep(3);                   /*這裡等待3秒,告訴你程式執行到什麼地方*/
scanf("%s",str1);        /*這兩句無需你再輸入,是對鍵盤盤緩衝區再掃描 */
scanf("%s",str2);        /*這兩句無需你再輸入,是對鍵盤盤緩衝區再掃描 */
printf("/n%s",str1);
printf("/n%s",str2);
return 0;
}
        輸入:I love  c!
輸出:
      I
love
c!
   那麼,怎麼來輸入一個有空格的字串? 
   用gets()當然可以,但我們同樣可以用 scanf(),因為,scanf()還有一個我們不常用的輸入格式符:    "%[]" 
   特別的:%*[width] [{h | l | I64 | L}]type 表示滿足該條件的被過濾掉,不會向目標引數中寫入值
支援集合操作:    //類似於  正則表示式 .

     %[a-z] 表示匹配a到z中任意字元,貪婪性(儘可能多的匹配)      //  或:  %[a-z1-9] 遇到非 a~z,1~9的則結束.
     %[aB'] 匹配a、B、'中一員,貪婪性                     //  以非 a,b,' 的字元為結束.
     %[^a] 匹配非a的任意字元,貪婪性      scanf("%[^a]",str);  

    //  輸入:  ffddssaaff   則提取 ffddss到字串str中. 即掃描到a就立即作為結束.
  故,我們可以用 scanf("%[^/n]",str); 來輸入一個有空格的字串. //輸入:l love c!回車,   則str中為 I love c!
     那麼 scanf("%*[^/]/%[^@]",str); 的作用呢???  //輸入一個字串,擷取 /到@之間的字串...

四: 解決鍵盤緩衝區的汙染問題.  //即 殘餘資訊 ,這個很重要吧.
    例如:
         scanf("%c",&c1); 
         scanf("%c",&c2); 
      當輸入: a回車b回車  輸出c1,c2的值:很明顯 c2不為b. 
    原因: 將c2用int表示出來,看看scanf()函式賦給C到底是什麼,結果是 c2=10 .
          ASCII值為10是什麼?換行即/n.
          我們每擊打一下"Enter"鍵,向鍵盤緩衝區發去一個“回車”(/r),一個“換行"(/n).
          在這裡 /r被scanf()函式處理掉了(姑且這麼認為吧^_^),而/n被scanf()函式“錯誤”地賦給了c.  
                  // 好像用getch()時,擊ENTER,接收的是回車,並且,它不從鍵盤緩衝區經過,即鍵盤緩衝區內容與其無關!!!!!
                       我做了個小試驗:   
                          scanf("%3s",str);
                          puts(str);
                          c1=getch();
                          printf("c1 %d/n",c1);
                          c2=getchar();
                          printf("c2 %d/n",c2);
                      // 輸入 aaabcdef 輸出aaa 輸入 A輸出: 65,98  看到了吧!!! 
                      // %3s只接收 aaa,然後輸入一個A,被getch()接收,輸出之後,getchar()繼續從緩衝區中取出 b ,明白了.
      解決這類問題最好的辦法是:  可以在兩個scanf()函式之後加個fflush(stdin);        //功能: 清除一個流 用法: int fflush(FILE *stream);
     另外,百度百科上還有另外一個方法:  用getchar()和getch()接收.     但是,通過上面那個實驗我們可以看到,getch()並不對緩衝區作處理,並不能處理scanf()的殘餘資訊. 
                //可以試一下:
                                           scanf("%c",c1);
                                           getch();   //假設用來接收換行.
                                           scanf("%c",c2);
               //輸入 A回車後 : c1值為 65,c2 為13 ,即換行.    

               //實際上,getch()會等著一個輸入.
               //而把getch()換為 getchar()後, 輸入 :A回車B回車,輸出 A B

五: 下面這段程式要輸入兩個數,程式才結束,而不是預期的一個,why?

#include<stdio.h>
  int main()
  {   
  int a;
  printf("input the data/n");
  scanf("%d/n",&a);       //這裡多了一個回車符/n,如果用scanf("%d  ",&a);    printf("%d",a);               //也會出現同樣問題.
  return 0;
  }
   //輸入: 11回車  後,沒有輸出,再輸入空格,回車,Tab 中任意多個,都沒有輸出,當輸入非空白字元時如 輸入 abc回車 ,才有輸出,輸出為11.
  //分析其原因(不一定準確,應該可以這麼解釋吧):
     scanf()是一個終端格式化輸入函式,也就是說按匹配 對 變數進行賦值!! 
 規則 : 例如 對於 " %d/n" :

          第一個空格可以與輸入緩衝區的 任意多個 空白字元匹配(包括空格,回車,Tab),當遇見第一個非空白字元時,結束其匹配,接著處理%d .
          %d可以與連續的數字符號進行匹配,當遇到第一個非數字符號時,結束匹配,若與其匹配的數字個數為0,則%d對應的變數值不變.//注意,%d與任意一個非int字元開始匹配失效,就算是 '.'也不例外,如輸入 12.30則 .30不會被讀取,而是留在緩衝區中. 
          同理,/n也要與 一個或者多個 Enter,Tab,space匹配,直到遇到第一個非 空白字元. 
          同樣 對於 "%d":  與緩衝區中第一個非空白字元開始進行匹配.
          但是"%c"是個例外,它與緩衝區中的第一個字元就匹配,不論空白與否,所以,處理 輸入的字元+Enter時,一定要請注意其中的Enter. 
         所以對上面的例子,輸入為: 11a時,回車一次就可以輸出11,但是不要忘了,緩衝區中還有 a和/n  !!!    

六:有關stdin, 事實上它就是一個標準輸入檔案, 為 File * 型別.

   因此, scanf("%s",str); 也就等價於 fscanf(stdin,"%s",str);

   但是 scanf()只能用來輸入 127以下個字元,也就是說,緩衝區只能裝下127個字元+'/',那為什麼 stdin->bsize又為 512呢?   //在TC下.