1.錯誤報告
? ? perror函數(shù)一種簡單、統(tǒng)一的方式報告錯誤。標準庫函數(shù)在一個外部整型變量errno(在errno.h中定義)中保存錯誤代碼之后把這個信息傳遞給用戶程序,提示操作失敗的準確原因。perror函數(shù)簡化向用戶報告這些特定錯誤的過程。定義于stdio.h中:
void?perror(?char?const?*message?);
? ? 如果message不是NULL并且指向一個非空的字符串,perror函數(shù)將打印這個字符串,后面跟一個分號和一個空格,然后打印出一條用于解釋errno當前錯誤代碼的信息。
? ? 注意,只有當一個庫函數(shù)失敗時,errno才會被設(shè)置。當函數(shù)成功運行時,errno的值不會被修改。這意味著我們不能通過測試errno的值來判斷是否有錯誤發(fā)生。反之,只有當被調(diào)用函數(shù)提示有錯誤發(fā)生時檢查errno的值才有意義。
?
2.終止執(zhí)行
void?exit(?int?status?);
? ? status參數(shù)返回給操作系統(tǒng),提示程序是否正常完成。預定義符號EXIT_SUCCESS和EXIT_FAILURE分別提示程序的終止是成功還是失敗。經(jīng)常會出現(xiàn)的是在調(diào)用perror函數(shù)之后再調(diào)用exit終止程序。
?
4.ANSI I/O概念
? ? stdio.h包含F(xiàn)ILE結(jié)構(gòu)的聲明,F(xiàn)ILE是一個數(shù)據(jù)結(jié)構(gòu),用于訪問一個流。編譯器保證一個程序同時最多可以打開文件的數(shù)量至少為FOPEN_MAX。這個常量包括了三個標準流,它的值至少為8。
?
5.流總覽
?
字符 getcharputchar 讀取(寫入)單個字符 文本行
gets
scanf
puts
printf
文本行未格式化的輸入(輸出)
格式化的輸入(輸出)
二進制數(shù)據(jù) fread fwrite 讀?。▽懭耄┒M制數(shù)據(jù)
????斜體為函數(shù)家族,指一組函數(shù)中的每個都執(zhí)行相同的基本任務(wù),只是方式稍有不同。這些函數(shù)的區(qū)別在于獲得輸入的來源或輸出寫入的地方不同。這些變種用于執(zhí)行下面任務(wù):
? ? 1.只用于stdin或stdout
? ? 2.隨作為參數(shù)的流使用
? ? 3.使用內(nèi)存中的字符串而不是流
?
getchar 字符輸入 fgets,getc getchar ① putchar 字符輸出 fputc,putc putchar ① gets 文本行輸入 fgets gets ② puts 文本行輸出 fputs puts ② scanf 格式化輸入 fscanf scanf sscanf printf 格式化輸出 fprintf printf sprintf
? ? ①對指針使用下標引用或間接訪問操作從內(nèi)存中獲得一個字符(或向內(nèi)存寫入一個字符)。
????②使用strcpy函數(shù)從內(nèi)存中讀取文本行(或向內(nèi)存中寫入文本行)
?
6.打開流
FILE?*fopen(?char?const?*name,?char?const?*mode?);
? ? 應該始終檢查fopen函數(shù)的返回值。
FILE?*fptr; fptr?=?fopen(?"data_name",?"r"?); if(?fptr?==?NULL?) { ????perror("data_name"); ????exit(EXIT_FAILURE); }
? ? freopen函數(shù)用于打開(重新打開)一個特定的文件流。原型如下:
FILE?*freopen(?char?const?*name,?char?const?*mode,?FILE?*stream?);
? ? 最后一個參數(shù)就是需要打開的流。它可能是一個先前從fopen函數(shù)返回的流,也可能是標準流。這個函數(shù)首先試圖關(guān)閉這個流,然后用指定的文件和模式重新打開這個流。如果打開失敗,返回NULL。如果打開成功,函數(shù)返回其第3個參數(shù)。
?
7.關(guān)閉流
? ? 流是使用函數(shù)fclose關(guān)閉的,關(guān)閉前會刷新緩沖區(qū)。如果只選成功,返回0,否則返回EOF。函數(shù)原型如下:
int?fclose(?FILE?*stream?);
? ? 只要操作成功后和操作失敗后的執(zhí)行指令不一樣,就應該進行錯誤檢查。
#include#includeint?main(?int?argc,?char?**argv) { int?exit_status?=?EXIT_SUCCESS; FILE?*input; //when?the?file?not?only?one while(*++argv?!=?NULL?) { //open?the?file input?=?fopent(?*argv,?"r"?); if(?input?==?NULL?) { perror(?*argv?); exit_status?=?EXIT_FAILURE; continue; } //deal?with?the?opened?file //close?the?file if(?fclose(?input?)?!=?0) { perror(?"fclcose"?); exit(?EXIT_FAILURE?); } } return?EXIT_SUCCESS; }
?
8.字符I/O??
int?fgetc(?FILE?*stream?); int?getc(?FILE?*stream?); int?getchar(?void?);
? ? getc和fgetc是從傳入的操作流中讀取,而getchar從標準輸入流中讀取。每個函數(shù)從流中讀取下一個字符,并把它作為函數(shù)的返回值返回。如果流中不存在更多字符,函數(shù)返回常量值EOF。返回int值的真正原因是為了運行函數(shù)報告文件的末尾(EOF),如果返回值為char,則256個字符中一定有一個被指定為EOF,導致如果文件中出現(xiàn)這個字符,字符后的內(nèi)容不會被讀取。
int?fputc(?int?character,?FILE?*stream?); int?putc(?int?character,?FILE?*stream?); int?putchar(?int?character?);
? ? fgetc和fputc是真正的函數(shù),但getc、putc、getchar和putchar都是通過#define指令定義的宏。
8.2撤銷字符I/O??
int?ungetc(?int?character,?FILE?*stream?);
? ? ungetc把一個先前讀入的字符返回到流中,使其在以后可以被重新讀入。每個流都允許至少一個字符被退回。如果一個流允許退回多個字符,那么這些字符再次被讀取的順序就以退回時的反序進行。注意把字符退回到流中和寫入到流中并不相同。與一個流相關(guān)聯(lián)的外部存儲并不受ungetc的影響。
? ? 警告:“退回”字符和流的當前位置有關(guān),如果用fseek、fsetpos或rewind函數(shù)改變了流的位置,所有退回的字符將被丟棄。
?
9.未格式化的行I/O
? ? 未格式化的行I/O簡單讀取或?qū)懭胱址?/p>
char?*fgets(?char?*buffer,?int?buffer_size,?FILE?*stream?); char?*gets(?char?*buffer?);
? ? fgets從指定的stream流中讀取字符并把它們復制到buffer(至少2個字節(jié),因為最后一個需要用來放NUL)中。當他讀到一個換行符并存儲到緩沖區(qū)中后不再讀取。如果緩沖區(qū)內(nèi)存儲的字符數(shù)達到buffer_size - 1個時也停止讀取。下一次調(diào)用fgets將從流的下一個字符開始讀取。在任何一種情況下,一個NUL字節(jié)將被添加到緩沖區(qū)所存儲的數(shù)據(jù)的末尾,使其成為一個字符串。
? ? 如果在任何字符讀取前就到達了文件尾,緩沖區(qū)就未進行修改,fgets函數(shù)返回一個NULL指針。否則,fgets就返回它的第一個參數(shù)(指向緩沖區(qū)的指針)。返回值通常只用于檢查是否到達了文件尾。
? ? gets和fgets主要不同在于讀取一行輸入時,并不在緩沖區(qū)中存儲結(jié)尾的換行符。由于gets沒有緩沖區(qū)參數(shù),所以無法判斷緩沖區(qū)長度。有可能緩沖區(qū)溢出。
?
int?fputs(?char?const?*buffer,?FILE?*stream?); int?puts(?char?const?*buffer?);
? ? 傳遞給fputs的緩沖區(qū)必須包含一個字符串,它的字符被寫入到流中。這個字符串預期以NUL字節(jié)結(jié)尾,這個字符串是逐字寫入。寫入出現(xiàn)錯誤時,fputs返回常量值EOF,否則返回一個非負值。
? ? puts與fputs的主要不同在于寫入一個字符串時,在字符串寫入之后向輸出再添加一個換行符。
#include#define?MAX_LINE_LENGTH?1024 void?copylines(?FILE?*input,?FILE?*output?) { char?buffer[MAX_LINE_LENGTH]; while(?(fgets(?buffer,?MAX_LINE_LENGTH,?input?)?!=?EOF?)?) fputs(?buffer,?output?); }
?
10.格式化的行I/O
????格式化的行I/O會執(zhí)行數(shù)字和其他變量的內(nèi)部和外部表示形式之間的轉(zhuǎn)換。
10.1 scanf函數(shù)家族
int?fscanf(?FILE?*stream,?char?const?*format,?...); int?scanf(?char?const?*format,?...); int?sscanf(?char?const?*string,?char?const?*format,?...);
? ? 原型中的省略號表示一個可變長度的指針列表。從輸入源讀取字符并根據(jù)format字符串給出的格式代碼對其進行轉(zhuǎn)換,將從輸入轉(zhuǎn)換而來的值逐個存儲到這些指針參數(shù)所指向的內(nèi)存位置。
? ? 當格式化字符串到達末尾或者讀取的輸入不在匹配格式字符串所指定的類型時,輸入停止。被轉(zhuǎn)換的參數(shù)數(shù)目作為函數(shù)的返回值返回。如果在任何輸入值被轉(zhuǎn)換前 文件就達到尾部,返回常量EOF。
? ? scanf函數(shù)家族中的format字符串參數(shù)可能包含的內(nèi)容有:
空白字符——與輸入中的0個或多個空白字符匹配,在處理過程中被忽略。 格式代碼——指定函數(shù)如何解釋接下來的輸入字符 其他字符——任何其他字符出現(xiàn)在格式字符串時,下一個輸入字符必須和它匹配。匹配后將被丟棄,不匹配函數(shù)不再讀取直接返回。
? ? scanf函數(shù)家族的格式代碼都以一個%開頭,后面可以是:
一個可選的星號——星號將使轉(zhuǎn)換后的值丟棄而不是存儲。 一個可選的寬度——寬度以一個非負的整數(shù)給出,限制將被讀取用于轉(zhuǎn)換的輸入字符的個數(shù)。如果未給出寬度,函數(shù)就連續(xù)讀入字符直到遇見輸入中的下一個空白字符。 一個可選的限定符 d,i,n short long ? o,u,x unsigned short unsigned long ? e,f,g ? double long double
? ? 4.格式代碼
?
c char *
讀取和存儲單個字符。前導的空白字符并不跳過。如果給出寬度,就讀取和存儲這個數(shù)目
的字符。字符后面不會再添加一個NUL字節(jié)。參數(shù)必須指向一個足夠大的字符數(shù)組
i
d
int *
一個可選的有符號整數(shù)被轉(zhuǎn)換。d把輸入解釋為10進制數(shù);i根據(jù)它的第1個字符決定值的
基數(shù),就像整型字面值常量的表達式一樣。
u
o
x
unsigned *
一個可選的有符號整數(shù)被轉(zhuǎn)換,但它按照無符號數(shù)存儲。如果使用u,值被解釋為十進制
數(shù),如果使用0,值被解釋為八進制數(shù);如果使用x,值被解釋為十六進制數(shù)。X與x同義
e
f
g
float *
期待一個浮點值。它的形式必須像一個浮點型字面值常量,但小數(shù)點并非必需。E和G分
別與e和g同義
s char *
讀取一串非空白字符。參數(shù)必須指向一個足夠大的字符數(shù)組。當發(fā)現(xiàn)空白輸入時就停止,
字符串后面會自動加上NUL終止符
[xxx] char *
根據(jù)給定組合的字符從輸入中讀取一串字符。參數(shù)必須指向一個足夠大的字符數(shù)組。當遇
到第1個不在給定組合中出現(xiàn)的字符時,輸入就停止。字符串后面會自動加上NUL終止符。
代碼%[abc]表示字符組包括a、b和c。如果列表中以一個^字符開頭,表示字符組合是所
列出的字符的補集,所以%[^abc]表示字符組為a、b、c之外的所有字符。右方括號也可以
出現(xiàn)在字符列表中,但它必須是列表的第1個字符。至于橫桿是否用來指定某個范圍的字符
(例如%[a-z]),則因編譯器而異
p void *
輸入預期為一串字符,諸如那些由printf函數(shù)的%p格式代碼所產(chǎn)生的輸出。它的轉(zhuǎn)換方式
因編譯器而異,但轉(zhuǎn)換結(jié)果將和按照上面描述的進行打印所產(chǎn)生的字符的值是相同的
n int *
d到目前為止通過這個scanf函數(shù)的調(diào)用從輸入讀取的字符數(shù)被返回。%n轉(zhuǎn)換的字符并不計
算在scanf函數(shù)的返回值之內(nèi)。它本身并不消耗任何輸入。
% ? 這個代碼與輸入中的一個%相匹配,該%符號將被丟棄。
? ? fscanf中將換行符也當做空白字符跳過,所以在使用fscanf時,在輸入中保持行邊界的同步有困難。
?
10.3printf家族
int?fprintf(?FILE?*stream,?char?const?*format,?...?); int?printf(?char?const?*format,?...?); int?sprintf(?char?*buffer,?char?const?*format,?...?);
? ? sprintf把它的結(jié)果作為一個NUL結(jié)尾的字符串存儲到指定的buffer緩沖區(qū),注意,緩沖區(qū)的大小不是sprintf的參數(shù),所以易出現(xiàn)內(nèi)存塊越位,可以通過對格式進行分析,看看最大可能出現(xiàn)的值被轉(zhuǎn)換后的輸出結(jié)果將由多長來防止越位。格式代碼以一個%開頭,后面:
?
零個或多個標志字符,用于修改有些轉(zhuǎn)換的執(zhí)行方式 一個可選的最小字段寬度 一個可選的精度 一個可選的修改符 轉(zhuǎn)換類型
?
c int 參數(shù)被裁剪為unsigned char類型并作為字符進行打印
d
i
int 參數(shù)作為一個十進制數(shù)打印。如果給出精度而且值的位數(shù)小于精度位數(shù),前面就用0填充
u
o
x,X
int 參數(shù)作為一個無符號值打印,u使用十進制,0使用八進制,x或X使用十六進制,兩者區(qū)別在于x使用abcdef,X使用ABCDEF
e
E
double 參數(shù)根據(jù)指數(shù)形式打印。小數(shù)點后面的位數(shù)由精度字段決定,默認是6 f double 參數(shù)按照常規(guī)的浮點格式打印。小數(shù)點后面的位數(shù)由精度字段決定,默認是6
g
G
double 如果指數(shù)≥-4但小于進度字段就用%f,否則使用指數(shù)格式 s char * 打印一個字符串 p void * 指針值被轉(zhuǎn)換為一串因編譯器而異的可打印字符。這個代碼主要和scanf中的%p代碼組合使用 n int * 獨特代碼,不產(chǎn)生任何輸出。相反,到目前為止函數(shù)所產(chǎn)生的輸出字符數(shù)目將被保存到對應的參數(shù)中 % (無)
打印一個%字符
- 值在字段中左對齊。默認情況是右對齊 0
d當數(shù)值是右對齊時,默認情況下是使用空格填充值左邊未使用的列,這個標志表示用0填充。它可用于
d,i,u,o,x,X,e,E,f,g和G代碼。使用d,i,o,u,x和X代碼時,如果給出精度,零標志就被忽略。如果格式代碼
中出現(xiàn)負號(-),零標志也沒有效果
+
當用于一個格式化某個符號值的代碼時,如果值非負,正號標志就會給它加上一個正號。如果該值為負,就像
往常一樣顯示一個負號。默認情況下,正號并不會顯示
空格
只用于轉(zhuǎn)換有符號值的代碼。當值非負時,這個標志把一個空格添加到它的開始位置。注意這個標志和正號標志
是相互排斥的。如果兩個同時給出,空格標志將被忽略
# 選擇某些代碼的另一種轉(zhuǎn)換形式。
11.二進制I/O
? ? 把數(shù)據(jù)寫到文件效率最高的方法就是用二進制寫入。
size_t?fread(?void?*buffer,?size_t?size,?size_t?count,?FILE?*stream?); size_t?fwrite(?void?*buffer,size_t?size,?size_t?count,?FILE?*stream?);
? ? buffer是一個指向保存數(shù)據(jù)的內(nèi)存位置的指針,size是緩沖區(qū)每個元素的字節(jié)數(shù),count是讀取或?qū)懭氲脑財?shù)。函數(shù)返回值是實際寫入或讀取的元素數(shù)目。
?
12.刷新和定位函數(shù)
int?fflush(?FILE?*stream?);
? ? fflush迫使一個輸出流的緩沖區(qū)內(nèi)的數(shù)據(jù)進行物理寫入,不管緩沖區(qū)是否被寫滿。例如,為保證調(diào)試信息實際打印出來,使用fflush。
long?ftell(?FILE?*stream?); int?fseek(?FILE?*stream,?long?offset,?int?from);
? ? from可以是SEEK_SET、SEEK_CUR和SEEK_END。在二進制流中,從SEEK_END進行定位可能不被支持。在文本流中,如果from是SEEK_CUR或SEEK_END,offset必須是0.如果from是SEEK_SET,offset必須是一個從同一個流中以前調(diào)用ftell返回的值。
?
SEEK_SET 從流的起始位置起offset個字節(jié),offset必須是一個非負值。 SEEK_CUR 從流的當前位置起offset個字節(jié),offset的值可正可負 SEEK_END 從流的尾部位置起offset個字節(jié),offset的值可正可負,如果為正,將定位到文件尾的后面
? ? 之所以存在這些限制,部分原因是文本流所執(zhí)行的行末字符映射。由于這個映射的存在,文本文件的字節(jié)數(shù)可能和程序?qū)懭氲淖止?jié)數(shù)不同。因此,一個可移植的程序不能根據(jù)實際寫入字符數(shù)的計算結(jié)果定位到文本流的一個位置。
? ? 用fseek改變一個流的位置會帶來三個副作用:
?
行末指示符被清除 如果在fseek之前使用ungetc把一個字符返回到流中,那么這個被退回的字符會被丟棄,因為在定位操作后,它不再是“下一個字符” 定位允許從寫入模式切換到讀取模式,或者回到打開的流以便更新。
void?rewind(?FILE?*stream?); int?fgetpos(?FILE?*stream,?fpos_t?*position?); int?fsetpos(?FILE?*stream,?fpos_t?*position?);
? ? rewind函數(shù)將讀/寫指針設(shè)置回指定流的起始位置,同時清除流的錯誤提示標志。
?
13.改變緩沖方式
? ? 在流上執(zhí)行的緩沖方式有時并不合適,下面兩個函數(shù)可以用于對緩沖方式進行修改。這兩個函數(shù)只有當指定的流被打開但還沒有在它上面執(zhí)行任何操作前才能被調(diào)用。
void?setbuf(?FILE?*stream,?char?*buf?); int?setvbuf(?FILE?*stream,?char?*buf,?int?mode,?size_t?size?);
? ? setbuf設(shè)置了另一個數(shù)組,用于對流進行緩沖。這個數(shù)組的字符長度必須是BUFSIZ(定義于stdio.h)。為一個流自行指定緩沖區(qū)可以防止I/O函數(shù)庫為它動態(tài)分配一個緩沖區(qū)。如果用一個NULL參數(shù)調(diào)用該函數(shù),setbuf將關(guān)閉流的所有緩沖方式,字符準確地將程序所規(guī)劃的方式進行讀取和寫入。
? ? setvbuf函數(shù)中,mode參數(shù)用于指定緩沖的類型。_IOFBF指定一個完全緩沖的流,_IONBF指定一個不緩沖的流,_IOLBF指定一個行緩沖的流。所謂行緩沖,就是每當一個換行符寫入到緩沖區(qū),緩沖區(qū)就進行刷新。
?
14.流錯誤函數(shù)
int?feof(?FILE?*stream?); int?ferror(?FILE?*stream?); void?clearerr(?FILE?*stream?);
? ? 如果流當前處于文件尾,feof函數(shù)返回真。這個狀態(tài)可以通過對流執(zhí)行fseek、rewind或fsetpos函數(shù)來清除。ferror函數(shù)報告流的錯誤狀態(tài),如果出現(xiàn)任何讀/寫錯誤函數(shù)就返回真。最后,clearerr函數(shù)對指定流的錯誤標志進行重置。
?
15.臨時文件
FILE?*tmpfile(?void?);
? ? 這個函數(shù)創(chuàng)建一個文件,當文件被關(guān)閉或程序終止時,這個文件便自動刪除。這個文件以wb+模式打開,這使它可用于二進制和文本數(shù)據(jù)。臨時文件的名字可以用tmpnam函數(shù)創(chuàng)建,它的原型如下:
char?*tmpnam(?char?*name?);
? ? 如果傳遞給函數(shù)的參數(shù)為NULL,那么這個函數(shù)便返回一個指向靜態(tài)數(shù)組的指針,該數(shù)組包含了被創(chuàng)建的文件名。否則,參數(shù)便假定是一個指向長度至少為L_tmpnam的字符數(shù)組的指針。在這個情況下,文件名在這個數(shù)組中創(chuàng)建,返回值就是這個參數(shù)。
?
16.文件操縱函數(shù)
int?remove(?char?const?*filename?); int?rename(?char?const?*oldname,?char?const?*newname?);
? ? 成功返回0值,失敗返回非0值。
?
?
數(shù)據(jù)類型 | 輸入 | 輸出 | 描述 | |
---|---|---|---|---|
家族名 | 目的 | 可用于所有的流 | 只用于stdin和stdout | 內(nèi)存中的字符串 |
格式碼 | h | l | L | |
代碼 | 參數(shù) | 含義 | ||
代碼 | 參數(shù) | 含義 | ||
標志 | 含義 | |||
from | 定位 |