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下.