scanf函数详解(下)
問題一
如何讓scanf()函數正確接受有空格的字符串?如: I love you!
#include <stdio.h>
int main()
{
? ?char str[80];
? ?scanf("%s",str);
? ?printf("%s",str);
? ?return 0;
}
輸入:I love you!
上述程序并不能達到預期目的,scanf()掃描到"I"后面的空格就認為對str的賦值結束,并忽略后面的"love you!".這里要注意是"love you!"還在鍵盤緩沖區(關于這個問題,網上我所見的說法都是如此,但是,我經過調試發現,其實這時緩沖區字符串首尾指針已經相等了,也就是說緩沖區清空了,scanf()函數應該只是掃描stdin流,這個殘存信息是在stdin中)。我們改動一下上面的程序來驗證一下:
#include <stdio.h>
#include<windows.h>
int main()
{
? ?char str[80], str1[80], str2[80];
? ?scanf("%s", str); /*此處輸入:I love you! */
? ?printf("%s\n", str);
? ?Sleep(5000); /*這里等待5秒,告訴你程序運行到什么地方*/
? ?/*不是sleep(5)
? ?1,函數名是Sleep不是sleep。
? ?2,C/C++中,unsigned Sleep(unsigned)應該是毫秒ms.
? ?*/
? ?scanf("%s", str1); /*這兩句無需你再輸入,是對stdin流再掃描*/
? ?scanf("%s", str2); /*這兩句無需你再輸入,是對stdin流再掃描*/
? ?printf("%s\n", str1);
? ?printf("%s\n", str2);
? ?return 0;
}
輸入:I love you!
輸出:
I
love
you!
好了,原因知道了,所以結論是:殘留的信息love you是存在于stdin流中,而不是在鍵盤緩沖區中。那么scanf()函數能不能完成這個任務?回答是:能!別忘了scanf()函數還有一個%[]格式控制符(如果對%[]不了解的請查看本文的上篇),請看下面的程序:
#include <stdio.h>
int main()
{
? ?char str[50];
? ?scanf("%49[^\n]", str); /* scanf("%s",string);不能接收空格符*/
? ?printf("%s\n", str);
? ?return 0;
}
問題二
鍵盤緩沖區殘余信息問題
#include <stdio.h>
int main()
{
? ?int a;
? ?char c;
? ?do
? ?{
? ? ? ?scanf("%d", &a);
? ? ? ?scanf("%c", &c);
? ? ? ?printf("a = %d c = %c\n", a, c);
? ? ? ?/* printf("c = %d\n", c); */
? ?} while(c != 'N');
? ?return 0;
}
scanf("%c", &c);這句不能正常接收字符,什么原因呢?我們用printf("c = %d\n", c);將C用int表示出來,啟用printf("c = %d\n", c);這一句,看看scanf()函數賦給C到底是什么,結果是c=10 ,ASCII值為10是什么?換行即\n.對了,我們每擊打一下"Enter"鍵,向鍵盤緩沖區發去一個“回車”(\r),一個“換行"(\n),在這里\r被scanf()函數處理掉了(姑且這么認為吧^_^),而\n被scanf()函數“錯誤”地賦給了c.解決辦法:可以在兩個scanf()函數之后加個fflush(stdin);,還有加getch(),getchar()也可以,但是要視具體scanf()語句加那個,這里就不分析了,讀者自己去摸索吧。但是加fflush(stdin);不管什么情況都可行。
(
函數名:fflush
功能:清除一個流
用法: intfflush(FILE *stream);
)
#include <stdio.h>
int main()
{
? ?int a;
? ?char c;
? ?do
? ?{
? ? ? ?scanf("%d", &a);
? ? ? ?fflush(stdin);
? ? ? ?scanf("%c", &c);
? ? ? ?fflush(stdin);
? ? ? ?printf("a=%d c=%c\n",a,c);
? ?} while(c!='N');
? ?return 0;
}
這里再給一個用“空格符”來處理緩沖區殘余信息的示例:
/版本1//運行出錯的程序///
#include <stdio.h>
int main()
{
? ?int i;
? ?char j;
? ?for (i = 0; i < 10; ++i)
? ?scanf("%c", &j); /*這里%前沒有空格*/
? ?printf("%c", j); /*在輸入十個字符之后
? ?return 0;
}
//
/版本2//使用了空格控制符后///
#include <stdio.h>
int main()
{
? ?int i;
? ?char j;
? ?for (i = 0; i < 10; ++i)
? ?scanf(" %c", &j);/*注意這里%前有個空格*/
? ?printf("%c", j);/*在輸入十個字符之后,驗證打印出來的字符是否是自己輸入的最后一個字符(即輸入的第十個字符)*/
? ?return 0;
}
接著,我們運行看看,首先,運行第一個版本(錯誤的程序)
我們輸入:0 1 2 3 4 5 6 7 8 9
結果是一個空字符
再運行第二個版本(正確的程序)
同樣輸入:0 1 2 3 4 5 6 7 8 9
這一次就顯示字符9,故此程序正確。
那么為什么第二個程序就正確呢,原因何在,在%前面加一個空格就這么有用,答案是肯定的,就是%前面的空格在起作用,讀者看看此文章的前面部分,在scanf的使用過程中應注意的問題中已經指出:“scanf()的格式控制串可以使用空白字符或其它非空白字符,使用空白字符會使scanf()函數在讀操作中略去輸入中的一個或多個空白字符。”
所以在%前面加上了空格(空格屬于空白字符,此外還有像制表符等也屬于空白字符),在輸入過程中,將略去輸入中的一個或多個空白字符,所以我們輸入的0 1 2 3 4 5 6 7 8 9這些字符中的空白字符就被略去了,字符9也就正確的打印出來了,這樣子解釋,相信大家都看明白勒吧!
問題三
如何處理scanf()函數誤輸入造成程序死鎖或出錯?
#include <stdio.h>
int main()
{
? ?int a, b, c;
? ?scanf("%d,%d", &a, &b);
? ?c = a + b; /*計算a+b*/
? ?printf("%d + %d = %d", a, b, c);
? ?return 0;
}
如上程序,如果正確輸入a,b的值,那么沒什么問題,但是,你不能保證使用者每一次都能正確輸入,一旦輸入了錯誤的類型,你的程序不是死鎖,就是得到一個錯誤的結果,呵呵,這可能所有人都遇到過的問題吧?解決方法:scanf()函數執行成功時的返回值是成功讀取的變量數,也就是說,你這個scanf()函數有幾個變量,如果scanf()函數全部正常讀取,它就返回幾。但這里還要注意另一個問題,如果輸入了非法數據,鍵盤緩沖區就可能還個有殘余信息問題。正確的例程:
#include <stdio.h>
int main()
{
? ?int a,b,c;
? ?while (scanf("%d,%d", &a, &b) != 2)
? ?fflush(stdin);
? ?c = a + b;
? ?printf("%d + %d = %d", a, b, c);
? ?return 0;
}
補充
fflush(stdin)這個方法在GCC下不可用。(在VC6.0下可以)
以下是C99對fflush函數的定義:
int fflush(FILE *stream);
如果stream指向輸出流或者更新流(update stream),并且這個更新流
執行的操作不是輸入,那么fflush函數將把任何未被寫入的數據寫入stream
指向的文件(如標準輸出文件stdout)。否則,fflush函數的行為是不確定的。
C和C++的標準里從來沒有定義過fflush(stdin)。
fflush(NULL)清空所有輸出流和上面提到的更新流。如果發生寫錯誤,fflush
函數會給那些流打上錯誤標記,并且返回EOF,否則返回0。
由此可知,如果stream指向輸入流(如stdin),那么fflush函數的行為是不確定的。故而使用
fflush(stdin)是不正確的,至少是移植性不好的。
可采用如下方法:
方法一:
/*此函數可以和scanf函數一起使用,但使用%c輸入時要注意,即此函數只能用于緩沖區非空的情況*/
#include <stdio.h>
void flush()
{
? ?char c;
? ?while ((c =getchar()) != '\n' && c != EOF) ;
}
int main()
{
? ?int a,b,c; /*計算a+b*/
? ?while (scanf("%d,%d", &a, &b) != 2)
? ?flush();
? ?c = a + b;
? ?printf("%d + %d = %d", a, b, c);
}
方法二:
使用getchar()代替fflush(stdin)
程序示例:
#include <stdio.h>
int main()
{
? ?int i, c;
? ?while (1 )
? ?{
? ? ? ?printf("Please input an integer: ");
? ? ? ?scanf("%d", &i);
? ? ? ?if (feof(stdin) || ferror(stdin))
? ? ? ?{
? ? ? ? ? ?/*如果用戶輸入文件結束標志(或文件已被讀完),*/
? ? ? ? ? ?/*或者發生讀寫錯誤,則退出循環*/
? ? ? ? ? ?/* do something */
? ? ? ? ? ?break;
? ? ? ?}
? ? ? ?/*沒有發生錯誤,清空輸入流。*/
? ? ? ?/*通過while循環把輸入流中的余留數據“吃”掉*/
? ? ? ?while ( (c =getchar()) != '\n' && c != EOF ) ; /*可直接將這句代碼當成fflush(stdin)的替代,直接運行可清除輸入緩存流*/
? ? ? ?/*使用scanf("%*[^\n]");也可以清空輸入流,*/
? ? ? ?/*不過會殘留\n字符。*/
? ? ? ?printf("%d\n", i);
? ?}
? ?return 0;
}
發展
使用scanf函數進行輸入,必須指定輸入的數據的類型和格式,不僅繁瑣復雜,而且很容易出錯.C++保留scanf只是為了和C兼容,以便過去用C語言寫的程序可以在C++的環境下運行。C++的編程人員都愿意使用cin進行輸入,很少使用scanf。
轉載于:https://blog.51cto.com/fancong/1293303
總結
以上是生活随笔為你收集整理的scanf函数详解(下)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 梦到刮鱼鳞象征着什么
- 下一篇: centos关于”running yum