C語(yǔ)言 輸入

2018-09-29 18:40 更新

流是什么?形象的比喻——水流,文件程序之間連接一個(gè)管道,水流就在之間形成了,自然也就出現(xiàn)了方向:可以流進(jìn),也可以流出。
便于理解,這么定義流: 流就是一個(gè)管道里面有流水,這個(gè)管道連接了文件和程序

UNIX系統(tǒng)認(rèn)為一切皆文件,所有的外部設(shè)備都被看做文件。

C語(yǔ)言系統(tǒng)定義了三個(gè)默認(rèn)的文件指針:
1、stdin 即標(biāo)準(zhǔn)輸入文件,與鍵盤連接。(即把鍵盤當(dāng)做文件)
2、stdout 即標(biāo)準(zhǔn)輸出文件,與屏幕連接。(即把屏幕當(dāng)做文件)
3、stderr 即標(biāo)準(zhǔn)出錯(cuò)文件,與屏幕連接。

注意:
stdout 和 stderr 是不同的設(shè)備描述符。
stdout是塊設(shè)備,stderr則不是。
對(duì)于塊設(shè)備,只有當(dāng)下面幾種情況下才會(huì)被輸入:1)遇到回車,2)緩沖區(qū)滿,3)flush被調(diào)用。而stderr則不會(huì)。

例:
fprintf(stdout,"hello-std-out");//不一定會(huì)輸出
fprintf(stderr,"hello-std-err");//一定會(huì)輸出

流的性質(zhì)

你只能順序地訪問(wèn)并提取流中的數(shù)據(jù),未提取的數(shù)據(jù)只能阻塞在流中,等待下次被訪問(wèn)或提取。
訪問(wèn)文件時(shí),流指針會(huì)自動(dòng)的改變當(dāng)前訪問(wèn)內(nèi)容的位置。即文件指針會(huì)隨著讀寫的流向而移動(dòng),它不是一直指向文件頭部的。

由于UNIX一切皆文件的思想,所以,所有操作文件的函數(shù)也都可以用來(lái)操作輸入/輸出設(shè)備。

輸入

從輸入流中讀取數(shù)據(jù)時(shí),停留在流中的字符可能會(huì)影響到下次的正確讀入。(這往往會(huì)造成隱藏的Bug)

丟棄輸入流中字符的方法:

  • 輸入結(jié)束后,把流中剩余的垃圾字符手動(dòng)讀掉

    while( (ch = getchar())!=EOF && ch!= ‘\n’)NULL ;

  • 用函數(shù)fflush清除一個(gè)流

    例:fflush(stdin);

fgets

從文件中讀取字符串
char fgets (char string, int n, FILE *fp) ;
功能:
從文件 fp 中讀取 n-1 個(gè)字符放入以 string 為首地址的空間里。讀入結(jié)束后,系統(tǒng)將自動(dòng)在最后加 ’\0’,并以 string 作為函數(shù)值返回。

gets( )和fgets( )不同,gets( )會(huì)丟棄換行符,并不把它存儲(chǔ)在緩沖數(shù)組中。
但gets( )對(duì)于輸入長(zhǎng)度沒(méi)有限制,很可能導(dǎo)致輸入長(zhǎng)度超過(guò)緩沖數(shù)組的長(zhǎng)度,導(dǎo)致緩沖區(qū)溢出。
【我們一般用fgets( )函數(shù)來(lái)接收用戶輸入。(這樣可允許用戶輸入任意字符)再在程序中分析用戶輸入,提取數(shù)據(jù)】

fgetc

fgetc( ) 接受一個(gè)輸入流作為參數(shù),它從這個(gè)流中讀取一個(gè)字符(可讀入回車等空白符),如果發(fā)生錯(cuò)誤或流已到結(jié)尾,則返回 EOF。
注意:其返回值是讀入的字符的 ASCII 碼值,是 int 型。

小心下面代碼中的隱式轉(zhuǎn)換的Bug

char ch ;
While( (ch=fgetc(stdin)) != EOF ) …..//錯(cuò)誤!

fgetc() 返回一個(gè)整型值而不是字符值,若把 fgetc 返回值存儲(chǔ)于 ch 中,將導(dǎo)致它被截?cái)?!然后這個(gè)被截?cái)嗟闹当惶嵘秊檎尾⑴c EOF 比較,循環(huán)會(huì)出錯(cuò)。
【用整形來(lái)定義一個(gè)字符變量更好!字符就是一個(gè)小整數(shù)】

scanf

scanf系列函數(shù)
int scanf( char const format, …… ) ;
int fscanf( FILE
fp, char const format, …… ) ;
int sscanf( char const
string, char const *format, ……) ;
//以上函數(shù)的讀入處理規(guī)則都相同,不同的是它們讀取的源不同,一個(gè)是從鍵盤讀取、一個(gè)從文件流讀取、一個(gè)從字符串讀取。(注意:字符串不是流,其沒(méi)有流指針保存讀取位置)

掃描集

一個(gè)字符序列可以用一個(gè)掃描集(scan set)來(lái)輸入。
掃描集是位于格式控制字符串中,以百分號(hào)開頭、用方括號(hào)[]括起來(lái)的一組字符.

檢查與掃描集中的字符相匹配的字符。一旦找到匹配的字符,那么這個(gè)字符將被存儲(chǔ)到掃描集對(duì)應(yīng)的實(shí)參(即指向一個(gè)字符數(shù)組的指針)中。只有遇到掃描集中沒(méi)有包含的字符時(shí),掃描集才會(huì)停止輸入字符。

如果輸入流中的第一個(gè)字符就不能與掃描集中包含的字符相匹配,那么只有空操作符被存儲(chǔ)到字符數(shù)組中。
(如果輸入的字符屬于方括號(hào)內(nèi)字符串中某個(gè)字符,那么就提取該字符;如果一經(jīng)發(fā)現(xiàn)不屬于就結(jié)束提取。該方法會(huì)自動(dòng)加上一個(gè)'\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]

對(duì)于字符串"abDEc123"如果想按照字母和數(shù)字讀到兩個(gè)字符串中就應(yīng)該是 "%[a-zA-Z]%[0-9]",buf1,buf2 ;

逆向掃描集

逆向掃描集還可以用來(lái)掃描那些沒(méi)有出現(xiàn)在掃描集中的字符。

創(chuàng)建一個(gè)逆向掃描集的方法是,在方括號(hào)內(nèi)掃描字符前面加一個(gè)“脫字符號(hào)”(^)。這個(gè)符號(hào)將使得那些沒(méi)有出現(xiàn)在掃描集中的字符被保存起來(lái)。只有遇到了逆向掃描集中包含的字符時(shí),輸入才會(huì)停止。(即取其后字符們的補(bǔ)集作為掃描集)

scanf(“%[^aeiou]”, str) ;
//即接受輸入流中的非元音字符。

用這種方法還可以解決scanf的輸入中不能有空格的問(wèn)題。只要用scanf("%[^\n]",str); 就可以了。很神奇吧。

【注意】
[]內(nèi)的字符串可以是1或更多字符組成。空字符集(%[])是違反規(guī)定的,可導(dǎo)致不可預(yù)知的結(jié)果。%[^]也是違反規(guī)定的。

指定域?qū)?/h3>

我們可以在scanf函數(shù)的轉(zhuǎn)換說(shuō)明符中指定域?qū)拋?lái)從輸入流中讀取特定數(shù)目的字符。

例:
scanf(“%2d%d”, &x, &y) ;
程序從輸入流中讀取一系列連續(xù)的數(shù)字,然后,將其前兩位數(shù)字處理為一個(gè)兩位的整數(shù),將剩余的數(shù)字處理成另外一個(gè)整數(shù)。

賦值抑制字符

即星號(hào) 。
賦值抑制字符使得 scanf 函數(shù)從輸入流中讀取任意類型的數(shù)據(jù),并將其丟棄,而不是將其賦值給一個(gè)變量。如果你想忽略掉某個(gè)輸入,使用在% 后使用 。

% [^=] 前面帶 號(hào)表示不保存變量。跳過(guò)符合條件的字符串

char s[]="notepad=1.0.0.1001";
char szfilename [32] = "" ;
int i = sscanf( s, "% [^=]", szfilename ) ;
// szfilename=NULL,因?yàn)闆](méi)保存
int i =sscanf( s, "% [^=]=%s", szfilename ) ;
// szfilename=1.0.0.1001

所有對(duì)%s起作用的控制,都可以用于%[]
比如"% [^\n]%c"就表示跳過(guò)一行,"%-20[^\n]"就表示讀取\n前20個(gè)字符。

把掃描集、賦值抑制符和域?qū)挼染C合使用,可實(shí)現(xiàn)簡(jiǎn)單的正則表達(dá)式那樣的分析字符串的功能。

返回值

scanf的返回值是讀入數(shù)據(jù)的個(gè)數(shù);
比如scanf("%d%d",&a,&b);讀入一個(gè)返回1,讀入2個(gè)返回2,讀入0個(gè)返回0;讀入錯(cuò)誤返回EOF即-1

安全性

你應(yīng)該非常小心的使用 scanf 因?yàn)樗赡軙?huì)是你的輸入緩沖溢出!
通常你應(yīng)該使用 fgets 和 sscanf 而不是僅僅使用 scanf,使用fgets 來(lái)讀取一行,然后用 sscanf 來(lái)解析這一行,就像上面演示的一樣。

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)