轉載自 yongahao---------------------------------------------------- | 問題描述一:(分析scanf()和gets()讀取字元) | ---------------------------------------------------- scanf(), getchar()等都是標準輸入函數,一般人都會覺得這幾個函數非常簡單,沒什麼特殊的。但是有時候卻就是因為使用這些函數出了問題,卻找不出其中的原因。下面先看一個很簡單的程式: 程式1:
#include <stdio.h> int main() { char ch1, ch2; scanf("%c", &ch1); scanf("%c", &ch2); printf("%d %d\n", ch1, ch2); return 0; } |
或者是:
#include <stdio.h> int main() { char ch1, ch2; ch1 = getchar(); ch2 = getchar(); printf("%d %d\n", ch1, ch2); return 0; } |
程式的本意很簡單,就是從鍵盤讀入兩個字元,然後列印出這兩個字元的ASCII碼值。可是執行程式後會發現除了問題:當從鍵盤輸入一個字元後,就列印出了結果,根本就沒有輸入第二個字元程式就結束了。例如使用者輸入字元'a', 列印結果是97,10。這是為什麼呢?【分析】 首先我們看一下輸入操作的原理,程式的輸入都建有一個緩衝區,即輸入緩衝區。一次輸入過程是這樣的,當一次鍵盤輸入結束時會將輸入的資料存入輸入緩衝區,而cin函數直接從輸入緩衝區中取資料。正因為cin函數是直接從緩衝區取資料的,所以有時候當緩衝區中有殘留資料時,cin函數會直接取得這些殘留資料而不會請求鍵盤輸入,這就是例子中為什麼會出現輸入語句失效的原因! 其實這裡的10恰好是斷行符號符!這是因為scanf()和getchar()函數是從輸入資料流緩衝區中讀取值的,而並非從鍵盤(也就是終端)緩衝區讀取。而讀取時遇到斷行符號(\n)而結束的,這個\n會一起讀入輸入資料流緩衝區的,所以第一次接受輸入時取走字元後會留下字元\n,這樣第二次的讀入函數直接從緩衝區中把\n取走了,顯然讀取成功了,所以不會再從終端讀取!這就是為什麼這個程式只執行了一次輸入操作就結束的原因! 這裡再插一句:ASCII碼中的10是line feed-\n-換行,將當前位置移到下一行開頭;13是carriage return-\r-斷行符號,將當前位置移到本行開頭。 在微軟的系統中,C程式處理文本是自動轉換這兩個符號的: 1、讀的時候將 \r\n 自動當作一個字元 \n 2、寫的時候寫一個 \n 自動轉換為 \r\n 造成一種誤解,好象 \n 就是斷行符號加換行 你可以向一個檔案中寫入一個 '\n',用十六進位編輯器開啟檔案,會發現有兩個位元組 0D 0A, 就是 \r\n了,你讀這個檔案的時候,也只會讀到一個字元 \n---------------------------------------------------- | 問題描述二:(分析scanf()和gets()讀取字串) | ----------------------------------------------------首先我們看一下scanf()讀取字串的問題: 程式2:
#include <stdio.h> int main() { char str1[20], str2[20]; scanf("%s",str1); printf("%s\n",str1); scanf("%s",str2); printf("%s\n",str2); return 0; } |
程式的功能是讀入一個字串輸出,再讀入一個字串輸出。可我們會發現輸入的字串中不能出現空格,例如: 測試一: Hello world! 輸出: Hello world!【分析】 到此程式執行完畢,不會執行第二次的讀取操作!這個問題的原因跟問題一類似,第一次輸入Hello world!後,字串Hello world!都會被讀到輸入緩衝區中,而scanf()函數取資料是遇到斷行符號、空格、TAB就會停止,也就是第一個scanf()會取出"Hello",而"world!"還在緩衝區中,這樣第二個scanf會直接取出這些資料,而不會等待從終端輸入。測試二: Hello[Enter] Hello[輸出] world[Enter] world[輸出]【分析】 程式執行了兩次從鍵盤讀入字串,說明第一次輸入結束時的斷行符號符被丟棄!即:scanf()讀取字串會捨棄最後的斷行符號符!我們再看一下gets()讀取字串的情況: 用scanf來讀取一個字串時,字串中是不可以出現空格的,一旦出現空格,後面的資料就會被捨棄從而殘留在緩衝區中。其實有另外一個函數是可以接受空格的,那就是gets(),下面我們看一下這個函數的應用,我們把程式2改動一下: 程式3:
#include <stdio.h> int main() { char str1[20], str2[20]; gets(str1); printf("%s\n",str1); gets(str2); printf("%s\n",str2); return 0; } |
測試: Hello world! [輸入] Hello world! [輸出] 12345 [輸入] 12345 [輸出]【分析】 顯然與上一個程式的執行情況不同,這次程式執行了兩次從鍵盤的讀入,而且第一個字串取了Hello world! 接受了空格符,而沒有像上一個程式那樣分成了兩個字串!所以如果要讀入一個帶空格符的字串時應該用gets(), 而不宜用scanf()! -------------------------------------------------------- | 問題描述三:(getchar()暫停程式,查看程式執行結果)| -------------------------------------------------------- 不知道大家有沒有遇到過這樣的問題,有的編譯器程式執行完後的結果介面不會停下而是一閃就沒了,以至於看不到執行結果。所以很多人在程式最後加上getchar()語句,目的是想讓程式執行完後停下來,等待從終端接收一個字元再結束程式。可是發現有時候這樣根本沒用,程式照樣跳出去了。這是為什麼呢? 【分析】 原因跟上面例子講的一樣,是因為輸入緩衝區中還有資料,所以getchar()會成果讀到資料,所以就跳出了!------------------ | 【總結】 | ------------------ 第一:要注意不同的函數是否接受空格符、是否捨棄最後的斷行符號符的問題! 讀取字元時: scanf()以Space、Enter、Tab結束一次輸入,不會捨棄最後的斷行符號符(即斷行符號符會殘留在緩衝區中); getchar()以Enter結束輸入,也不會捨棄最後的斷行符號符; 讀取字串時: scanf()以Space、Enter、Tab結束一次輸入 gets()以Enter結束輸入(空格不結束),接受空格,會捨棄最後的斷行符號符!第二:為了避免出現上述問題,必須要清空緩衝區的殘留資料,可以用以下的方法解決: 方法1:C語言裡提供了函數清空緩衝區,只要在讀資料之前先清空緩衝區就沒問題了! 這個函數是fflush(stdin)。 方法2:自己取出緩衝區裡的殘留資料。 (說實話這個語句我也沒看懂,呵呵!為什麼格式控制是這樣的!希望高手指點一下!) scanf("%[^\n]",string);//直至遇到斷行符號結束; scanf("%[^\t^\n]",string);//直至遇到tab或斷行符號結束; 再轉一篇~~~ scanf函數的一般形式: scanf(格式控制,地址表列) int scanf(char *format,argument,...); “格式控制”的含義同printf函數;“地址表列”是由若干個地址組成的表列,可以是變數的地址,或字串首地址。 scanf()函數返回成功賦值的資料項目數,出錯時則返回EOF。 格式字元說明: %a,%A 讀入一個浮點值(僅C99有效) %c 讀入一個字元 %d 讀入十進位整數 %i 讀入十進位,八進位,十六進位整數 %o 讀入八進位整數 %x,%X 讀入十六進位整數 %c 讀入一個字元 %s 讀入一個字串,遇空格、定位字元或分行符號結束。 %f,%F,%e,%E,%g,%G 用來輸入實數,可以用小數形式或指數形式輸入。 %p 讀入一個指標 %u 讀入一個無符號十進位整數 %n 至此已讀入值的等價字元數 %[] 掃描字元集合 %% 讀%符號 附加格式說明字元表修飾符說明: L/l 長度修飾符 輸入"長"資料 h 長度修飾符 輸入"短"資料 W 整型常數 指定輸入資料所佔寬度 * 表示本輸入項在讀入後不賦值給相應的變數 scanf的傳回值: scanf的傳回值有後面的參數決定,scanf("%d%d", &a, &b); 1、如果a和b都被成功讀入,那麼scanf的傳回值就是2 2、如果只有a被成功讀入,傳回值為1 3、如果a和b都未被成功讀入,傳回值為0 4、如果遇到錯誤或遇到end of file,傳回值為EOF。 使用scanf函數時應該注意的問題 1、sacnf()中的變數必須使用地址。 2、scanf()的格式控制串可以使用其它非空白字元,但在輸入時必須輸入這些字元。 3、在用"%c"輸入時,空格和“逸出字元”均作為有效字元。 問題一:scanf()函數不能正確接受有空格的字串?如: I love you!
#include <stdio.h> int main() { char str[80]; scanf("%s",str); printf("%s",str); return 0; } |
輸入:I love you! 輸出:scanf()函數接收輸入資料時,遇以下情況結束一個資料的輸入:(不是結束該scanf函數,scanf函數僅在每一個資料域均有資料,並按斷行符號後結束)。 ① 遇空格、“斷行符號”、“跳格”鍵。 ② 遇寬度結束。 ③ 遇非法輸入。 所以,上述程式並不能達到預期目的,scanf()掃描到"I"後面的空格就認為對str的賦值結束,並忽略後面的"love you!".這裡要注意是"love you!"還在鍵盤緩衝區(關於這個問題,網上我所見的說法都是如此,但是,我經過調試發現,其實這時緩衝區字串首尾指標已經相等了,也就是說緩衝區清空了,scanf()輸入一個單詞沒問題,輸入兩個單詞無論加入多少個getchar()都會彈出?
#include <stdio.h>int main(void) { char name[40]; printf("what's your name?"); scanf("%s", name); printf("%s,hello boy", name); getchar(); getchar(); return 0; } |
請告訴我是什麼“道理” scanf讀取時 是遇到空格就停止讀取!也就是說 如果你鍵入得是harry potter 那麼實際上scanf只能擷取harry這個單詞 後面的“ potter”會在停在輸入資料流中那麼你就要最少要加9個getchar(一個空格 6個單詞字母 一個分行符號還有一個待輸入游標)才能看到運行結果 用scanf函數輸入字串時,字串中不能含有空格,否則將以空格作為串的結束符。 為了避免這種情況,可多設幾個字元數組分段存放含空格的串。 想知道誰在起作用很簡單,因為你輸入幾個單詞的時候,發現只有第一個單詞能夠顯示出來,猜想scanf(%s)提取字元竄的條件是Enter(換行--輸入結束)或者Space(空格),而且還可以做個極端的測試,就你一開始只輸入換行或者空格,發現程式永遠不會執行printf,而只要我們輸入一竄連續字元+一個空格之後,不論我們輸入什麼,Enter之後,printf執行完之後都是一開始的一竄連續字元,因此我們還可以知道,scanf(%s)提取完成的語句格式是一開始遇到的一連竄字元;而由於執行getchar(),之前printf輸出的是scanf所提取的字元竄,而之後的字元或者空格包括斷行符號將會被getchar()調用,其實你的程式兩個getchar()的作用只相當於一個getchar()的作用,因為第一個只不過讀取的是斷行符號符,也就是你輸入一個字元竄之後的斷行符號鍵而已;因此當你輸入幾個用空格隔開的字元竄的時候,無用的getchar()的個數應該等於(斷行符號數)1+n1(空格數)+n2(除第一個連續有形字元竄的其他字元數);所以當你輸入兩個甚至幾個名字的時候,getchar()是遠遠不夠用的; |