流是什么?形象的比喻——水流,文件
和程序
之間連接一個管道,水流就在之間形成了,自然也就出現(xiàn)了方向:可以流進,也可以流出。
便于理解,這么定義流: 流就是一個管道里面有流水,這個管道連接了文件和程序。
UNIX系統(tǒng)認為一切皆文件,所有的外部設備都被看做文件。
C語言系統(tǒng)定義了三個默認的文件指針:
1、stdin 即標準輸入文件,與鍵盤連接。(即把鍵盤當做文件)
2、stdout 即標準輸出文件,與屏幕連接。(即把屏幕當做文件)
3、stderr 即標準出錯文件,與屏幕連接。
注意:
stdout 和 stderr 是不同的設備描述符。
stdout是塊設備,stderr則不是。
對于塊設備,只有當下面幾種情況下才會被輸入:1)遇到回車,2)緩沖區(qū)滿,3)flush被調(diào)用。而stderr則不會。
例:
fprintf(stdout,"hello-std-out");//不一定會輸出
fprintf(stderr,"hello-std-err");//一定會輸出
你只能順序地訪問并提取流中的數(shù)據(jù),未提取的數(shù)據(jù)只能阻塞在流中,等待下次被訪問或提取。
訪問文件時,流指針會自動的改變當前訪問內(nèi)容的位置。即文件指針會隨著讀寫的流向而移動,它不是一直指向文件頭部的。
由于UNIX一切皆文件的思想,所以,所有操作文件的函數(shù)也都可以用來操作輸入/輸出設備。
從輸入流中讀取數(shù)據(jù)時,停留在流中的字符可能會影響到下次的正確讀入。(這往往會造成隱藏的Bug)
丟棄輸入流中字符的方法:
輸入結束后,把流中剩余的垃圾字符手動讀掉
while( (ch = getchar())!=EOF && ch!= ‘\n’)NULL ;
例:fflush(stdin);
從文件中讀取字符串
char fgets (char string, int n, FILE *fp) ;
功能:
從文件 fp 中讀取 n-1 個字符放入以 string 為首地址的空間里。讀入結束后,系統(tǒng)將自動在最后加 ’\0’,并以 string 作為函數(shù)值返回。
gets( )和fgets( )不同,gets( )會丟棄換行符,并不把它存儲在緩沖數(shù)組中。
但gets( )對于輸入長度沒有限制,很可能導致輸入長度超過緩沖數(shù)組的長度,導致緩沖區(qū)溢出
。
【我們一般用fgets( )函數(shù)來接收用戶輸入。(這樣可允許用戶輸入任意字符)再在程序中分析用戶輸入,提取數(shù)據(jù)】
fgetc( ) 接受一個輸入流作為參數(shù),它從這個流中讀取一個字符(可讀入回車等空白符),如果發(fā)生錯誤或流已到結尾,則返回 EOF。
注意:其返回值是讀入的字符的 ASCII 碼值,是 int 型。
小心下面代碼中的隱式轉(zhuǎn)換的Bug
char ch ;
While( (ch=fgetc(stdin)) != EOF ) …..//錯誤!
fgetc() 返回一個整型值而不是字符值,若把 fgetc 返回值存儲于 ch 中,將導致它被截斷!然后這個被截斷的值被提升為整形并與 EOF 比較,循環(huán)會出錯。
【用整形來定義一個字符變量更好!字符就是一個小整數(shù)】
scanf系列函數(shù)
int scanf( char const format, …… ) ;
int fscanf( FILE fp, char const format, …… ) ;
int sscanf( char const string, char const *format, ……) ;
//以上函數(shù)的讀入處理規(guī)則都相同,不同的是它們讀取的源不同,一個是從鍵盤讀取、一個從文件流讀取、一個從字符串讀取。(注意:字符串不是流,其沒有流指針保存讀取位置)
一個字符序列可以用一個掃描集(scan set)來輸入。掃描集是位于格式控制字符串中,以百分號開頭、用方括號[]括起來的一組字符.
檢查與掃描集中的字符相匹配的字符。一旦找到匹配的字符,那么這個字符將被存儲到掃描集對應的實參(即指向一個字符數(shù)組的指針)中。只有遇到掃描集中沒有包含的字符時,掃描集才會停止輸入字符。
如果輸入流中的第一個字符就不能與掃描集中包含的字符相匹配,那么只有空操作符被存儲到字符數(shù)組中。
(如果輸入的字符屬于方括號內(nèi)字符串中某個字符,那么就提取該字符;如果一經(jīng)發(fā)現(xiàn)不屬于就結束提取。該方法會自動加上一個'\0'到已經(jīng)提取的字符后面。)
例:
char str[512] ;
printf(“Enter string:\n”) ;
scanf(“%[aeiou]”, str) ;
//程序使用掃描集[aeiou]在輸入流中尋找元音字符,直到遇到非元音字符。
我們還可以用縮寫a-z表示abcd….xyz字母集。
scanf(“%[a-z]”, str) ;
同理,也可以用縮寫0-9 縮寫A-Z。
想只取字母,那就可以寫成 %[A-Za-z]
對于字符串"abDEc123"如果想按照字母和數(shù)字讀到兩個字符串中就應該是 "%[a-zA-Z]%[0-9]",buf1,buf2 ;
逆向掃描集還可以用來掃描那些沒有出現(xiàn)在掃描集中的字符
。
創(chuàng)建一個逆向掃描集的方法是,在方括號內(nèi)掃描字符前面加一個“脫字符號”(^)。這個符號將使得那些沒有出現(xiàn)在掃描集中的字符被保存起來。只有遇到了逆向掃描集中包含的字符時,輸入才會停止。(即取其后字符們的補集作為掃描集)
scanf(“%[^aeiou]”, str) ;
//即接受輸入流中的非元音字符。
用這種方法還可以解決scanf的輸入中不能有空格的問題。只要用scanf("%[^\n]",str); 就可以了。很神奇吧。
【注意】
[]內(nèi)的字符串可以是1或更多字符組成。空字符集(%[])是違反規(guī)定的,可導致不可預知的結果。%[^]也是違反規(guī)定的。
我們可以在scanf函數(shù)的轉(zhuǎn)換說明符中指定域?qū)拋韽妮斎肓髦凶x取特定數(shù)目的字符。
例:
scanf(“%2d%d”, &x, &y) ;
程序從輸入流中讀取一系列連續(xù)的數(shù)字,然后,將其前兩位數(shù)字處理為一個兩位的整數(shù),將剩余的數(shù)字處理成另外一個整數(shù)。
即星號 。
賦值抑制字符使得 scanf 函數(shù)從輸入流中讀取任意類型的數(shù)據(jù),并將其丟棄,而不是將其賦值給一個變量。如果你想忽略掉某個輸入
,使用在% 后使用 。
% [^=] 前面帶 號表示不保存變量。跳過符合條件的字符串。
char s[]="notepad=1.0.0.1001";
char szfilename [32] = "" ;
int i = sscanf( s, "% [^=]", szfilename ) ;
// szfilename=NULL,因為沒保存
int i =sscanf( s, "% [^=]=%s", szfilename ) ;
// szfilename=1.0.0.1001
所有對%s起作用的控制,都可以用于%[]
比如"% [^\n]%c"就表示跳過一行,"%-20[^\n]"就表示讀取\n前20個字符。
把掃描集、賦值抑制符和域?qū)挼染C合使用,可實現(xiàn)簡單的正則表達式那樣的分析字符串的功能。
scanf的返回值是讀入數(shù)據(jù)的個數(shù);
比如scanf("%d%d",&a,&b);讀入一個返回1,讀入2個返回2,讀入0個返回0;讀入錯誤返回EOF即-1
你應該非常小心的使用 scanf 因為它可能會是你的輸入緩沖溢出!
通常你應該使用 fgets 和 sscanf 而不是僅僅使用 scanf,使用fgets 來讀取一行,然后用 sscanf 來解析這一行,就像上面演示的一樣。
更多建議: