由scanf說起之1:scanf函數和回車、空格 及其返回值


scanf和回車的關系:

  先看一個我們經常使用的代碼:

  char c;   scanf("%c", &c);

  scanf 的各種格式中,%d、%c、%s三種最常用,通常都是以回車作為一次輸入的結束。由於對字符解析方式及字符特點不同(如數字中一般不會出現空格回車等特殊字符)不同,%d格式一般不會在連續輸入時,遇到問題。而%s和%c卻會出現各種各樣的問題,比如回車問題,空格問題等。

我們已經習慣了上面的代碼,再看下面存在連續輸入時的情況

  int i;   char c;   scanf("%d", &i);   scanf("%c", &c);

  如果我們為i賦值為3,並以回車的方式結束對i的賦值。再按常規思路為c賦值時,就會發現並不能為c賦值,並且c自動賦值為十進制的10,也就是回車鍵。why???

要回答這個問題,先看scanf函數的功能和工作原理

  scanf函數這個函數的作用是從標准輸入設備獲取輸入值,並存儲到參數列表中指針所指向的內存單元,如果讀入成功,函數會返回讀入成功的數據的個數;scanf函數的結束通常有3種,遇到空格、回車或者tab鍵為常用的結束方式;或者按照格式控制符的指定來控制結束,如%5d類的格式;遇到非法輸入也會自動結束。

對常用的三種格式,結束符號分別如下:

%d格式輸入,默認分隔符是所有的 white-spaces(空格、回車、制表);

%c格式輸入,則按ASCII字符考慮,無分隔符。可能會受到之前輸入的影響,必要時用fflush(stdin);清除緩沖區;

%s 是 字符串格式,默認分隔符是所有的 white-spaces,輸入后自動加入結束符"\0"。

  繼續上面的由於連續輸入帶來的問題,往深了說,就涉及到緩沖區了。scanf函數是以刪除的方式從緩沖區讀取數據(緩沖區中存儲來自標准輸入的數據)。如果緩沖區是空的,就阻塞之,等待從鍵盤輸入;並且scanf還能對數字輸入忽略先導的空白符,如\n\t和空格等(注意,對字符輸入並不忽略先導字符,這個也是很自然的道理,因為\n\t和空格在字符中都是合法的字符)。

  scanf的緩沖機制和對字符的處理方式就造成了scanf對字符%c和字符串%s的讀取時的各種意外。比如上面的例子,回頭分析這段代碼:

  輸入了i的值為3然后按回車,當前緩沖區中數據為”3\n”,由回車的作用scanf開始從緩沖區中讀取一個%d控制的數據,也就是3,此時緩沖區中還剩”\n”;對下一個scanf函數的格式控制是%c,這個情況下並不忽略先導的空白字符\n,而是直接賦給字符c了。想要查看緩沖區的內容,stdin[s1] ,如果想要查看當前stdin中的內容,一般方法都比較忤逆,可以嘗試使用文件操作freopen將stdin中的數據重定向到另外一個file*中。這個另辟一文。

  如何解決這個緩沖區和字符解析的問題呢?既然緩沖區有我們不需要的東西,那就清除緩沖區。微軟系統中是fflush(stdin)函數可以清除緩沖區,而有的編譯系統並沒有定義對stdin的fflush操作,就把stdin中的數據讀出來:

1)  可以使用fgets()函數,這個函數沒有編譯器的限制;

2)  或者把緩沖區中多余的東西交給別的函數,如getchar(),具體代碼為 while( (c = getchar() ) != '/n' && c != EOF );

3) 上面的方法基於原理,但有點麻煩,尤其是遇到字符%c和字符串%s對回車的處理時。C還提供了gets()函數解決了這個問題,gets()函數是不論中間有什么字符,一律讀進來,直到遇到回車符;

4)  C++中,還可以操作stdin的指針,stdin是一個File*類型的數據結構,使stdin->_IO_read_ptr = stdin->_IO_read_end;。但是C中不可以。

 

scanf和空格鍵

  前面就有說,scanf函數,根據格式的不同,對空白字符的處理也不同。%d格式下,對空白字符不敏感,通常都是作為結束符的;對%c來說,對回車符比較敏感,空格的做為一個普通字符處理的;對%s來說,回車和空格都是當前函數的結束字符,由於緩沖區stdin機制,這里又要特別注意 空格和回車對%s的影響。

  %s默認分隔符是所有的 white-spaces,輸入后自動加入結束符"\0",使其成為一個字符串(之所以加上\0,是和字符數組char[]的結束符有關的,C中是沒有string這個類型的,是使用char[]結構實現字符串)。值得注意的是,即使輸入字符的長度足夠,%s是寧願舍棄輸入字符,也要把/0加上去的,作為字符串的結束。並且,scanf會忽略緩沖區開頭的空格,知道遇到一個非空格字符,才開始向內存中讀取數據。

  比如,我們想要輸入”The C Programming Language.\n”,中間的空格怎么處理呢?

  其中一個解決方法是使用gets函數,這個函數是以回車符作為輸入結束的標志的;還有一種解決方式是:scanf("%[^\n]", c)

  char c[15];   scanf("%[^\n]", c[s2] );

  只是scanf("%[^\n]", c);和gets這兩種處理方式都是不忽略所有的空格,包括緩沖區開頭的空格;這一點和scanf("%s",c);的處理方式不同,%s的方式,忽略緩沖區開頭的空格。

 

另:scanf的返回值

  scanf()函數返回的值為:正確按指定格式輸入變量的個數;也即能正確接收到值的變量個數。在類型匹配錯誤的時候,以非正常的方式退出。可以利用scanf函數的返回值判斷輸入是否正確,並進行流程控制:

     int i = 0; char c1[15]; while((scanf("%c", &c1[i])!=EOF) && i<14) { i++; }

 


 [s1]stdin是標准輸入,一般針對鍵盤,是個FILE* 類型的數據。

 [s2]這里注意使用c 還是 &c:

          c是個數組,數組 即代表了數組的首地址,所以可以使用c;

          &c,表示數組c的地址,也就是指首地址,所以也是可以的。但通常c更符合邏輯一點。這在讀取%s類型的數據時可以說明這一點:

                  scanf(“%s”, c);

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM