標(biāo)準(zhǔn)IO

2016-02-01

標(biāo)準(zhǔn)io

標(biāo)準(zhǔn)io處理了很多細(xì)節(jié)谷婆,例如緩存分配辽聊,優(yōu)化長度執(zhí)行io等。

流和file對象

之前我們了解所有的io函數(shù)都是針對于文件描述符的身隐,而對于標(biāo)準(zhǔn)io贾铝,他們的操作是圍繞流進(jìn)行的。當(dāng)用表針io庫打開或者創(chuàng)建一個文件時垢揩,我們已經(jīng)使得一個流與一個文件相結(jié)合。
當(dāng)打開一個流時斑匪,標(biāo)準(zhǔn)io函數(shù)fopen返回一個指向FILE對象的指針锋勺。該對象是一個結(jié)構(gòu)它包含了io庫為管理該流所需要的所有信息:文件描述符狡蝶,指向流緩存的指針贪惹,緩存的長度寂嘉,當(dāng)前在緩存中的字符數(shù),出錯標(biāo)志等等

標(biāo)準(zhǔn)輸入硼端、標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)出錯

對于一個進(jìn)程預(yù)定義了三個流他們自動的可為進(jìn)程使用:標(biāo)準(zhǔn)輸入寓搬、標(biāo)準(zhǔn)輸出、和標(biāo)準(zhǔn)出錯曼尊。這三個標(biāo)準(zhǔn)io流同于預(yù)定義文件指針stdin,stdout,stderr加以引用脏嚷。這三個文件指針同樣定義在頭文件<stdio.h>中

緩存

標(biāo)準(zhǔn)io提供緩存的目的是盡可能減少使用read和write調(diào)用的數(shù)量瞒御。它也對每個io流自動地今次那個緩存管理,避免了應(yīng)用程序需要考慮這一點帶來的麻煩趾唱。它提供三種類型的緩存

  • 全緩存蜻懦。這種情況下,當(dāng)填滿標(biāo)準(zhǔn)io緩存后才進(jìn)行實際io操作悠咱。對于駐在磁盤上的文件通常是由標(biāo)準(zhǔn)io庫實施全緩存征炼。在一個流上執(zhí)行第一次io操作時,相關(guān)標(biāo)準(zhǔn)io函數(shù)通常調(diào)用malloc獲取使用的緩存眼坏。緩存可由標(biāo)準(zhǔn)io例程自動執(zhí)行刷新(例如當(dāng)填滿一個緩存)或者可以調(diào)用fflush刷新一個流酸些。
  • 行緩存檐蚜。這種情況下囤屹,當(dāng)輸入和輸出中遇到新行標(biāo)準(zhǔn)(或者緩存區(qū)寫滿)io庫執(zhí)行io操作。這允許我們一次輸出一個字符乡括,但只有在寫了一行后才進(jìn)行實際的io操作智厌。當(dāng)流設(shè)計一個終端時,典型地使用行緩存敷扫。任何時候只要通過標(biāo)準(zhǔn)輸入輸出庫要求從一個不帶緩存的流或者一個行緩存流得到數(shù)據(jù)诚卸,那么就會造成刷新所有行緩存流
  • 不帶緩存。標(biāo)準(zhǔn)io庫不對字符進(jìn)行緩存卒密。如果用標(biāo)準(zhǔn)io函數(shù)寫若干字符到不帶緩存的流中棠赛,則相當(dāng)于用write系統(tǒng)調(diào)用函數(shù)將這些字符寫至相關(guān)聯(lián)的打開文件上睛约。標(biāo)準(zhǔn)出錯流不帶緩存,使得錯誤信息盡快顯示出來辩涝。ANSIC要求:當(dāng)且僅當(dāng)標(biāo)準(zhǔn)輸入和輸出并不涉及交互作用設(shè)備時,他們才是全緩存的捉邢。標(biāo)準(zhǔn)出錯時全緩存的沧踏。

對于任何一個給定的流,如果我們并不喜歡這些系統(tǒng)默認(rèn)秘案,則可以調(diào)用下列函數(shù)更改緩存類型
#include <stdio.h>
void setbuf(FILE *fp, char *buf);
int setvbuf(FILE *fp, char *buf, int mode, size_t size) mode _IOFBF 全緩存 _IOLBF 行緩存 _IONBF不帶緩存
這些函數(shù)一定要在流已被打開后調(diào)用,而且也應(yīng)在對流執(zhí)行任何一個其他操作之前調(diào)用
如果指定一個不帶緩存的流則忽略buf和size赚导。如果指定全緩存或者行緩存赤惊,則buf和size可以可選地指定一個緩存及其長度。
一般而言我們應(yīng)由系統(tǒng)選擇緩存長度圈暗,并自動分配緩存裕膀。這樣處理是,標(biāo)準(zhǔn)io庫在關(guān)閉此流時將自動釋放此緩存寸齐。
任何時候抄谐,我們可以強(qiáng)制刷新一個流蛹含。
#include<stdio.h>
int fflush(FILE *fp)
此函數(shù)使該流所有未寫的數(shù)據(jù)都被傳遞至內(nèi)核.fp 為NULL刷新所有輸出流。

打開流

FILE *fopen(const char *pathname, const char *type)
FILE *freopen(const char *pathname, const char *type, FILE *fp)
FILE *fdopen(int filedes, const char *type)
freopen在一個特定的流上打開一個指定的文件挣惰,如果該流已經(jīng)打開則先關(guān)閉憎茂。此函數(shù)一般用于講一個指定的文件大開衛(wèi)一個預(yù)定義的流锤岸。
fdopen 去一個現(xiàn)存的文件描述符,并使一個標(biāo)準(zhǔn)io流與該描述符相結(jié)合拳氢。此函數(shù)通常用于由創(chuàng)建管道和網(wǎng)絡(luò)通信函數(shù)獲得的描述符蛋铆。因為這些特殊類型的文件不能用標(biāo)準(zhǔn)io fopen打開必須先調(diào)用設(shè)備專用函數(shù)獲得一個文件描述符,然后調(diào)用fdopen使一個標(biāo)準(zhǔn)io與該描述符結(jié)合留特。type指定io流的讀寫方式。
對于fdopen ,type參數(shù)中因為描述符已經(jīng)打開苟蹈,所以fdopen為寫打開并不截短文件右核。另外標(biāo)準(zhǔn)io添加方式也不能用于創(chuàng)建文件。當(dāng)添加類型打開一個文件后贺喝,則每次都將數(shù)據(jù)寫到文件的當(dāng)前結(jié)尾躏鱼,如若有多個進(jìn)程用都采用這種模式打開同一文件,那么來自每個進(jìn)程的數(shù)據(jù)都將正確寫到文件中扳抽。
當(dāng)以讀寫類型打開一個文件時殖侵,如果中間沒有fflush,fseek fsetpos 或者rewind則輸入后面不能直接輸出,且輸出后面不能直接輸入
調(diào)用fclose關(guān)閉一個打開的流
在該文件關(guān)閉之前楞陷,刷新緩存中的輸出數(shù)據(jù)茉唉,緩存中的輸入數(shù)據(jù)被丟棄,如果是自動分配的緩存艾凯,則釋放此緩存懂傀。

讀寫流

一旦打開了流,則可以在三種不同類型的非格式化io中進(jìn)行選擇

  • 每次一個字符的io恃泪。一次讀寫一個字符犀斋,如果流帶緩存,則標(biāo)準(zhǔn)io處理所有緩存
  • 每次一行的io览效,使用fgets和fputs一次讀寫一行,當(dāng)調(diào)用fgets時應(yīng)說明能處理的he最大行長
  • fread 和 fwrite函數(shù)支持這種類型的io禁筏,每次io操作或?qū)懩撤N數(shù)量的對象衡招,而每個對象有指定的長度。

輸入函數(shù)

以下三個函數(shù)可以用于一次讀一個字符
#include<stdio.h>
int getc(FILE *fp)
int fgetc(FILE *fp)
int getchar(void)
函數(shù)getchar等同于getc前兩個函數(shù)的區(qū)別是getc可被實現(xiàn)為宏州刽,而fgetc則不能實現(xiàn)為宏這意味著:
1浪箭、getc參數(shù)不應(yīng)當(dāng)是具有副作用的表達(dá)式
2、因為fgetc一定是一個函數(shù)匹表,所以可以得到其地址宣鄙。這就允許將fgetc的地址作為一個參數(shù)傳遞給另一個函數(shù)
3冻晤、調(diào)用fgetc所需的時間可能長于getc因為調(diào)用函數(shù)通常所需的時間長于調(diào)用宏。檢驗下<stdio.h>頭文件的大多數(shù)實現(xiàn)從中可以見getc是一個宏鼻弧,其編碼就有較高的工作效率攘轩。

這三個函數(shù)以unsigned char 類型轉(zhuǎn)換為int的方式返回下一個字符。說明為不帶符號的理由是鹉胖,如果最高位1也不會使返回值為負(fù)够傍。要求整型返回值的理由是挠铲,這樣就可以返回所有可能的字符再加上一個已發(fā)生錯誤或者已到達(dá)文件結(jié)尾的指示值。由于EOF經(jīng)常為-1.這就意味著不能將這三個函數(shù)的返回值放在一個字符變量中安聘,還要將返回值與EOF進(jìn)行比較。不管出錯還是到達(dá)文件結(jié)尾三個函數(shù)返回同樣值丘喻,為了區(qū)分這兩種情況必須調(diào)用ferror或者feof.clearerr函數(shù)清除這兩個標(biāo)識
從一個流讀之后念颈,可以調(diào)用ungetc將字符再送回流中。雖然ANSI C支持任意數(shù)量字符送回的實現(xiàn)嗡靡,但是它要求任何一種實現(xiàn)都要支持一個字符的回送功能窟感。一次成功的送回會清除EOF標(biāo)識

輸出函數(shù)

int putc(int c , FILE *fp)
int fputc(int c, FILE *fp)
int putchar(int c);
若成功則返回c出錯則為EOF

每次一行io

char *fgets(char *buf, int n, FILE *fp)
char *gets(char *buf)
這兩個函數(shù)都制定了緩存地址讀入的行將送入其中柿祈,gets從標(biāo)準(zhǔn)輸入讀,而fgets從指定流讀取黑滴。對于fgets必須指定緩存的長度n紧索,此函數(shù)一直讀到下一個新行為止,但不超過n-1如果讀不完下次繼續(xù)本行晚缩。
fputs(const char *strr, FILE *fp)
puts(const char *str)
函數(shù)fputs將一個以null符終止的字符串寫到指定流媳危,終止符null補(bǔ)寫出。fputs并不一定產(chǎn)生一個新行鸣皂。puts一定會產(chǎn)生一個新行

二進(jìn)制io

如果為二進(jìn)制io我們更愿意一次讀或者寫整個結(jié)構(gòu)暮蹂。
size_t fread(void *ptr, size_t size, size_t nobj, FILE *fp)
size_t fwrite(const void *ptr, size_t size, size_t nobj, FILE *fp)
eg:讀寫一個二進(jìn)制數(shù)

  float data[10]
  if (fwrite(&data[2], sizeof(float), 4, fp) != 4)
    err_sys("fwrite error");

讀寫一個結(jié)構(gòu)

 struct{
     short count;
     long total;
     char name[NAMESIZE];
     } item;
     if (fwrite(&item, sizeof(item), 1, fp) != 1) 
     error_sys("fwrite error");

在不同系統(tǒng)之間交換二進(jìn)制數(shù)據(jù)的實際解決方法是使用較高層的協(xié)議

定位流

有兩種方法定位標(biāo)準(zhǔn)io流

  • ftell和fseek它們都假定文件的位置可以存放在一個長整型中仰泻。
  • fgetpos和fsetpos。這兩個函數(shù)引進(jìn)了一個新的數(shù)據(jù)類型fpos_t被啼。要移植到非unix系統(tǒng)上的應(yīng)用應(yīng)當(dāng)使用fgetpos和fsetpos

long ftell(FILE *fp)
int fseek(FILE *fp, long offset, int whence) whence SEEK_SET文件起始位置 SEEK_CUR表示從當(dāng)前文件位置,SEEK_END文件結(jié)尾
void rewind(FILE *fp)
對于一個二進(jìn)制文件泡挺,其位置指示器是從文件起始位置開始度量命浴,并以字節(jié)為計量單位。
rewind將一個流設(shè)置到文件起始位置
int fgetpos(FILE *fp, fpos_t *pos)
int fsetpos(FILE *fp, jconst fpos_t *pos) 成功返回0

格式化io

格式化輸出

執(zhí)行格式化輸出處理的是三個printf函數(shù)
int printf(const char *format, ...)
int fprintf(FILE *fp, const char *format, ...)
int sprintf(char *buf, const char *format, ...)
printf將格式化數(shù)據(jù)寫到標(biāo)準(zhǔn)輸出稚新, fprintf寫到指定流, sprintf將格式化的字符送入數(shù)組buf中跪腹,sprintf在改數(shù)組的尾端自動添加一個null

格式化輸入

int scanf(const char *format, ...)
int fscanf(FILE *fp, const char *format)
int sscanf(const char *buf, const char *format, ...)

實現(xiàn)細(xì)節(jié)

每個io流都有一個與其關(guān)聯(lián)的文件描述符褂删,可以對一個流調(diào)用fileno以獲得其描述符
int fileno(FILE *fp)
如果要調(diào)用dup 或 fcntl等函數(shù),則需要此函數(shù)

臨時文件

標(biāo)準(zhǔn)庫提供了兩個函數(shù)以幫助創(chuàng)建臨時文件
char *tmpnam(char *ptr)
FILE *tmpfile(coid)
tmpnam產(chǎn)生一個與現(xiàn)在文件名不同的一個有效路徑名字字符串冲茸,每次調(diào)用都產(chǎn)生一個不同路徑名屯阀。
tmpfile創(chuàng)建一個臨時二進(jìn)制文件,在關(guān)閉該文件或者程序結(jié)束時將自動刪除這種文件
tempnam是tmpnam的一個變體轴术,它允許調(diào)用者為所產(chǎn)生的路徑名指定目錄和前綴难衰。
char *tempnam(const char * directory, const char *prefix)
對于目錄:

  • 如果定義了環(huán)境變量TMPDIR則用其作為目錄
  • 如果參數(shù)directory非NULL,則用其作為目錄
  • 將<stdio.h>中的字符串P_tmpdir用作為目錄
  • 將本地目錄,通常是/tmp用作目錄

如果prefix非NULL逗栽,則他應(yīng)該是最多5個字符的字符串,其用作文件名的頭幾個字符

標(biāo)準(zhǔn)io替代軟件

標(biāo)準(zhǔn)io中一個效率不足之處是需要復(fù)制的數(shù)據(jù)量彼宠,當(dāng)使用每次一行函數(shù)fgets和fgets時通常需要重復(fù)復(fù)制兩次數(shù)據(jù):一次是內(nèi)核和標(biāo)準(zhǔn)io緩存之間鳄虱,一個是在標(biāo)準(zhǔn)io緩存和用戶程序中的行緩存中,fio(快速io庫)避免了這一點凭峡,其方法是使杜一航的函數(shù)返回指向改行的指針拙已。Korn和Vo說明了標(biāo)準(zhǔn)io庫的另一種替代版,sfio.這一軟件包速度上與fio相近摧冀,通潮蹲伲快于標(biāo)準(zhǔn)io。sfio提供了一些新的特征:推廣了io流索昂,使其不僅可以代表文件建车,也可代表存儲區(qū),可以編寫處理模塊并以棧的方式將其壓入io流楼镐。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末癞志,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子框产,更是在濱河造成了極大的恐慌凄杯,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件秉宿,死亡現(xiàn)場離奇詭異戒突,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)描睦,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進(jìn)店門膊存,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人忱叭,你說我怎么就攤上這事隔崎。” “怎么了韵丑?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵爵卒,是天一觀的道長。 經(jīng)常有香客問我撵彻,道長钓株,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任陌僵,我火速辦了婚禮轴合,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘碗短。我一直安慰自己受葛,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布偎谁。 她就那樣靜靜地躺著奔坟,像睡著了一般。 火紅的嫁衣襯著肌膚如雪搭盾。 梳的紋絲不亂的頭發(fā)上咳秉,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天,我揣著相機(jī)與錄音鸯隅,去河邊找鬼澜建。 笑死,一個胖子當(dāng)著我的面吹牛蝌以,可吹牛的內(nèi)容都是我干的炕舵。 我是一名探鬼主播,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼跟畅,長吁一口氣:“原來是場噩夢啊……” “哼咽筋!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起徊件,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤奸攻,失蹤者是張志新(化名)和其女友劉穎蒜危,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體睹耐,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡辐赞,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了硝训。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片响委。...
    茶點故事閱讀 38,117評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖窖梁,靈堂內(nèi)的尸體忽然破棺而出赘风,到底是詐尸還是另有隱情,我是刑警寧澤纵刘,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布邀窃,位于F島的核電站,受9級特大地震影響彰导,放射性物質(zhì)發(fā)生泄漏蛔翅。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一位谋、第九天 我趴在偏房一處隱蔽的房頂上張望山析。 院中可真熱鬧,春花似錦掏父、人聲如沸笋轨。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽爵政。三九已至,卻和暖如春陶缺,著一層夾襖步出監(jiān)牢的瞬間钾挟,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工饱岸, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留掺出,地道東北人。 一個月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓苫费,卻偏偏與公主長得像汤锨,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子百框,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,877評論 2 345

推薦閱讀更多精彩內(nèi)容