
作者:楊老師,華清遠見教育科技集團講師。
在linux下的(de)(de)C程(cheng)序編程(cheng)中對(dui)文件的(de)(de)IO操作有標(biao)準IO和(he)文件IO兩種操作類(lei)型。標(biao)準IO是帶緩沖(chong)的(de)(de)IO屬(shu)于庫(ku)函數,文件IO是不(bu)帶緩沖(chong)的(de)(de)屬(shu)于系統調用。系統調用和(he)庫(ku)函數之間(jian)的(de)(de)區別如下圖1-1

圖 1-1
一、 標準IO
標準IO的緩沖類型分(fen)為:全緩沖、行緩沖、不緩沖三種類型。
在標(biao)準(zhun)(zhun)IO中對文(wen)件操(cao)作默認是(shi)(shi)全緩(huan)沖(chong)的,對于磁盤(pan)文(wen)件的緩(huan)沖(chong)區(qu)是(shi)(shi)由標(biao)準(zhun)(zhun)IO庫(ku)中的函數通過(guo)malloc來(lai)獲(huo)得的。
在標準(zhun)IO中與終(zhong)端相關的通常(chang)是行緩(huan)(huan)沖,但是如果標準(zhun)輸入、標準(zhun)輸出不涉及交互時(shi)也可以設成全緩(huan)(huan)沖。
標準出錯是不帶緩沖的,這(zhe)樣就可以將出錯信息實時的顯示出來。
這三種緩沖類型(xing)可(ke)以通過(guo)void setbuf(FILE * f p, char * b u f) 和(he)int setvbuf(FILE * f p, char * b u f, int m o d e, size_t s i z e) 這兩個函(han)數(shu)(shu)進行修改。比如(ru)(ru),在linux下標準(zhun)輸出(chu)的(de)(de)行緩沖的(de)(de)大(da)小(xiao)默認設(she)置(zhi)為1024個字(zi)節(jie),如(ru)(ru)果你想把它擴大(da)2048個字(zi)節(jie),那么就可(ke)在應(ying)用(yong)程序中調(diao)用(yong)setbuf函(han)數(shu)(shu)來設(she)置(zhi)。設(she)置(zhi)后再(zai)向終端(duan)進行輸出(chu)時如(ru)(ru)果不加其他刷新條件,那么直到輸出(chu)超過(guo)2048個字(zi)節(jie)時數(shu)(shu)據才會(hui)被刷新出(chu)來。 而setvbuf函(han)數(shu)(shu)還可(ke)以明(ming)確的(de)(de)指(zhi)定緩沖類型(xing),比如(ru)(ru)_IOFBF(全緩沖)、_IOLBF(行緩沖)、_IONBF(無緩沖)。
標準(zhun)IO中的(de)freopen函數是用(yong)來將一(yi)個(ge)(ge)流進行(xing)重(zhong)新(xin)定向的(de)。如果該流之(zhi)(zhi)前已經被重(zhong)新(xin)定向,那么(me)freopen函數將會清除之(zhi)(zhi)前的(de)定向并且使用(yong)本次(ci)定向。通(tong)常(chang)用(yong)來將一(yi)個(ge)(ge)文件打開為stdin、stdout、stderr三個(ge)(ge)流。
fdopen函數(shu)是(shi)用(yong)(yong)來將一個已經打(da)開的文件描述和(he)一個流進行(xing)關聯。因為管道(dao)和(he)socket套(tao)接字等描述符是(shi)不可(ke)以用(yong)(yong)標準(zhun)IO的函數(shu)打(da)開的,因此可(ke)以調用(yong)(yong)此函數(shu)進行(xing)關聯,然后就可(ke)以用(yong)(yong)標準(zhun)IO的函數(shu)進行(xing)操作(zuo)了。
標準IO中(zhong)gets函數是用來從(cong)終端讀(du)入(ru)字(zi)符串(chuan)的,因為它并不會(hui)檢測字(zi)符串(chuan)的長度,因此如(ru)果緩(huan)沖的大小設(she)置(zhi)不合適會(hui)導致未知的錯(cuo)誤。gets函數從(cong)終端上讀(du)到的字(zi)符串(chuan)中(zhong)后一個字(zi)符其實是‘\n’字(zi)符,然后gets函數又將‘\n’字(zi)符轉換成了‘\0’字(zi)符,所以gets函數會(hui)將后的換行符讀(du)走。
相對于(yu)gets函數(shu)來說fgets函數(shu)是安全(quan)的(de)因為(wei)它會檢測讀(du)入字符的(de)長度(du)。當使用fgets函數(shu)從終端上讀(du)入字符時有兩種情況:
第(di)一種:如果終端上輸(shu)入的字符(fu)個(ge)數小于fgets函數指定的size-1個(ge),那(nei)么它(ta)也會將終端上‘\n’字符(fu)讀走(zou)并且在(zai)結尾添加(jia)上‘\0’字符(fu)。
第二(er)種:如果終(zhong)端上輸(shu)入的字符個數大于fgets函數指定的sisz-1個,那么它就(jiu)不會(hui)(hui)讀走終(zhong)端上的‘\n’字符,而且(qie)末尾始終(zhong)會(hui)(hui)添加‘\0’字符。
二、文件IO
文件IO是(shi)不帶緩沖,是(shi)因為每個(ge)read和write都會調用內核(he)中(zhong)的系統調用。當使(shi)用文件IO的讀寫(xie)函數對(dui)文件進行操作后,其實數據也并(bing)不會實時的寫(xie)入磁盤,只是(shi)放(fang)(fang)到(dao)(dao)了內核(he)緩沖區(qu)中(zhong),如果該(gai)緩沖區(qu)沒有寫(xie)滿(man),那么不會將該(gai)緩沖區(qu)放(fang)(fang)入到(dao)(dao)輸出隊(dui)列。只有緩沖區(qu)滿(man)了或者內核(he)需要該(gai)緩沖區(qu),那么才將該(gai)緩沖區(qu)加入到(dao)(dao)輸出隊(dui)列中(zhong),等待其到(dao)(dao)達隊(dui)首時才可以進行實際的IO操作。
在使用(yong)(yong)open函數打開(kai)文(wen)件時,如果(guo)使用(yong)(yong)了O_SYNC標志,那(nei)么當(dang)調用(yong)(yong)的(de)write函數在返回時就已經將數據(ju)寫入(ru)到磁盤上了。而如果(guo)是調用(yong)(yong)sync()函數,那(nei)么當(dang)該函數返回時數據(ju)并沒(mei)有寫到磁盤上而僅僅是被(bei)放到了輸出隊列中。
文(wen)(wen)件(jian)IO中的(de)(de)(de)(de)定位函數(shu)lseek可(ke)以(yi)返回一個(ge)(ge)負的(de)(de)(de)(de)文(wen)(wen)件(jian)指(zhi)針偏(pian)移,因為對于(yu)某些設備(bei)文(wen)(wen)件(jian)允許負的(de)(de)(de)(de)偏(pian)移量。因此在(zai)對lseek函數(shu)進行出錯判斷時(shi),不可(ke)以(yi)用是否小于(yu)0,而應該用是否等于(yu)-1。當(dang)時(shi)用lseek函數(shu)生成(cheng)一個(ge)(ge)空(kong)洞文(wen)(wen)件(jian)時(shi),其實文(wen)(wen)件(jian)的(de)(de)(de)(de)空(kong)洞部分是不占磁盤塊的(de)(de)(de)(de)。使用ls –ls命(ming)令查看兩個(ge)(ge)字節大(da)小相同但(dan)是一個(ge)(ge)有(you)空(kong)洞,一個(ge)(ge)沒有(you)空(kong)洞的(de)(de)(de)(de)文(wen)(wen)件(jian)時(shi)會發(fa)現(xian),有(you)空(kong)洞的(de)(de)(de)(de)文(wen)(wen)件(jian)所占的(de)(de)(de)(de)磁盤塊要(yao)少(空(kong)洞的(de)(de)(de)(de)大(da)小要(yao)足夠(gou)大(da)時(shi)才可(ke)以(yi)看到現(xian)象),如(ru)下(xia)圖(tu)1-2:

圖 1-2