C程序員使用指針來(lái)管理用于讀取和寫入數(shù)據(jù)的流。
流只是文件或硬件設(shè)備,如顯示器或打印機(jī)。
要指向并管理C中的文件流,請(qǐng)使用名為FILE的數(shù)據(jù)結(jié)構(gòu)。
類型FILE的指針就像任何其他變量一樣創(chuàng)建。
#include <stdio.h> int main(void) { //create 3 file pointers FILE *pRead; FILE *pWrite; FILE *pAppend; return 0; } //end main
上面的代碼創(chuàng)建了三個(gè)名為pRead,pWrite和pAppend的FILE指針變量。
每個(gè)FILE指針可以打開和管理單獨(dú)的數(shù)據(jù)文件。
文件處理的基本組件是打開,處理和關(guān)閉數(shù)據(jù)文件。
打開一個(gè)文件應(yīng)該有錯(cuò)誤檢查和/或處理。
未能測(cè)試文件打開的結(jié)果將導(dǎo)致不必要的程序結(jié)果。
要打開數(shù)據(jù)文件,請(qǐng)使用標(biāo)準(zhǔn)輸入/輸出庫(kù)函數(shù)fopen()。
fopen()函數(shù)返回一個(gè)指向FILE指針的FILE指針。
#include <stdio.h>
main()
{
FILE *pRead;
pRead = fopen("file1.dat", "r");
} //end main
該程序使用fopen()函數(shù)以只讀方式打開名為file1.dat的數(shù)據(jù)文件。
fopen()函數(shù)返回一個(gè)FILE指針?lè)祷氐絧Read變量。
fopen()函數(shù)有兩個(gè)參數(shù):文件名,第二個(gè)參數(shù)告訴fopen()如何打開文件。
下表列出了使用fopen()打開文本文件的幾個(gè)常用選項(xiàng)。
模式 | 描述 |
---|---|
r | 打開文件進(jìn)行閱讀 |
w | 創(chuàng)建寫入文件; 丟棄任何以前的數(shù)據(jù) |
a | 寫入文件結(jié)尾(追加) |
上面的代碼生成以下結(jié)果。
打開文件后,應(yīng)確保文件指針成功返回。
要測(cè)試fopen()的返回值,請(qǐng)測(cè)試條件中的NULL值,如下所示。
#include <stdio.h>
main()
{
FILE *pRead;
pRead = fopen("file1.dat", "r");
if ( pRead == NULL )
printf("\nFile cannot be opened\n");
else
printf("\nFile opened for reading\n");
} //end main
以下條件
if ( pRead == NULL )
可以縮短下一個(gè)條件。
if ( pRead )
如果pRead返回非NULL,則if條件為true。如果pRead返回NULL,則該條件為false。
成功打開和處理文件后,應(yīng)使用函數(shù)fclose()關(guān)閉文件。
fclose()函數(shù)使用FILE指針來(lái)刷新流并關(guān)閉文件。
fclose()函數(shù)將FILE指針名稱作為參數(shù)。
fclose(pRead);
上面的代碼生成以下結(jié)果。
以下代碼顯示如何使用函數(shù)fscanf()和feof()讀取文件的內(nèi)容并檢查文件的EOF(文件結(jié)尾)標(biāo)記。
以下程序讀取名為.dat的數(shù)據(jù)文件,直到讀取文件結(jié)束標(biāo)記為止。
#include <stdio.h>
main()
{
FILE *pRead;
char name[10];
pRead = fopen("names.dat", "r");
if ( pRead == NULL )
printf("\nFile cannot be opened\n");
else
printf("\nContents of names.dat\n\n");
fscanf(pRead, "%s", name);
while ( !feof(pRead) ) {
printf("%s\n", name);
fscanf(pRead, "%s", name);
} //end loop
} //end main
在成功打開names.dat之后,我使用fscanf()函數(shù)來(lái)讀取文件中的一個(gè)字段。
fscanf()函數(shù)與scanf()函數(shù)類似,但與FILE流有關(guān)。
它有三個(gè)參數(shù):一個(gè)FILE指針,一個(gè)數(shù)據(jù)類型和一個(gè)變量來(lái)存儲(chǔ)檢索到的值。
讀取記錄后,printf()函數(shù)顯示文件中的數(shù)據(jù)。
要讀取多個(gè)記錄,請(qǐng)使用可以讀取所有記錄的循環(huán),直到滿足條件。
要讀取所有記錄,直到滿足文件結(jié)尾,請(qǐng)使用feof()函數(shù)。
使用非運(yùn)算符(!),可以將FILE指針傳遞給feof()函數(shù)并循環(huán),直到達(dá)到文件結(jié)尾標(biāo)記時(shí)函數(shù)返回非零值。
通過(guò)向第二個(gè)參數(shù)提供記錄中每個(gè)字段的一系列類型說(shuō)明符,fscanf()也可以讀取包含多個(gè)字段的記錄。
例如,下一個(gè)fscanf()函數(shù)希望讀取兩個(gè)名為name和hobby的字符串。
fscanf(pRead, "%s%s", name, hobby);
%s類型說(shuō)明符將讀取一系列字符,直到找到空白,包括空白,新行或選項(xiàng)卡。
fscanf()函數(shù)的其他有效類型說(shuō)明符列在表11.3中。
類型 | 描述 |
---|---|
c | 單字符 |
d | 十進(jìn)制整數(shù) |
e,E,f,g,G | 浮點(diǎn) |
o | 八進(jìn)制整數(shù) |
s | 字符串 |
u | 無(wú)符號(hào)十進(jìn)制整數(shù) |
x,X | 十六進(jìn)制整數(shù) |
上面的代碼生成以下結(jié)果。
以下代碼顯示如何讀取多個(gè)字段。
#include <stdio.h>
main()
{
FILE *pRead;
char name[10];
char hobby[15];
pRead = fopen("hobbies.dat", "r");
if ( pRead == NULL )
printf("\nFile cannot be opened\n");
else
printf("\nName\tHobby\n\n");
fscanf(pRead, "%s%s", name, hobby);
while ( !feof(pRead) ) {
printf("%s\t%s\n", name, hobby);
fscanf(pRead, "%s%s", name, hobby);
} //end loop
} //end main
上面的代碼生成以下結(jié)果。
fprintf()函數(shù)接收FILE指針,數(shù)據(jù)類型列表以及將數(shù)據(jù)寫入數(shù)據(jù)文件的值列表。
通過(guò)用記錄分隔記錄中的每個(gè)字段,我可以使用以下程序輕松地讀取相同的記錄。
#include <stdio.h>
main()
{
FILE *pRead;
char fName[20];
char lName[20];
char id[15];
float gpa;
pRead = fopen("students.dat", "r");
if ( pRead == NULL )
printf("\nFile not opened\n");
else {
//print heading
printf("\nName\t\tID\t\tGPA\n\n");
//read field information from data file and store in variables
fscanf(pRead, "%s%s%s%f", fName, lName, id, &gpa);
//print variable data to standard output
printf("%s %s\t%s\t%.2f\n", fName, lName, id, gpa);
fclose(pRead);
} //end if
} //end main
使用帶有w參數(shù)值的fopen()打開數(shù)據(jù)文件將會(huì)擦除文件中存儲(chǔ)的任何以前的數(shù)據(jù)。
使用a屬性在文件末尾附加數(shù)據(jù)。
上面的代碼生成以下結(jié)果。
將信息附加到數(shù)據(jù)文件中,包括使用fopen()中的一個(gè)屬性打開一個(gè)寫入文件,并將數(shù)據(jù)寫入現(xiàn)有文件的末尾。
但是,如果該文件不存在,則會(huì)按照f(shuō)open()語(yǔ)句中的指定創(chuàng)建一個(gè)新的數(shù)據(jù)文件。
以下代碼演示將記錄附加到現(xiàn)有數(shù)據(jù)文件。
#include <stdio.h>
void readData(void);
main()
{
FILE *pWrite;
char name[10];
char hobby[15];
printf("\nCurrent file contents:\n");
readData();
printf("\nEnter a new name and hobby: ");
scanf("%s%s", name, hobby);
//open data file for append
pWrite = fopen("hobbies.dat", "a");
if ( pWrite == NULL )
printf("\nFile cannot be opened\n");
else {
//append record information to data file
fprintf(pWrite, "%s %s\n", name, hobby);
fclose(pWrite);
readData();
} //end if
} //end main
void readData(void)
{
FILE *pRead;
char name[10];
char hobby[15];
//open data file for read access only
pRead = fopen("hobbies.dat", "r");
if ( pRead == NULL )
printf("\nFile cannot be opened\n");
else {
printf("\nName\tHobby\n\n");
fscanf(pRead, "%s%s", name, hobby);
//read records from data file until end of file is reached
while ( !feof(pRead) ) {
printf("%s\t%s\n", name, hobby);
fscanf(pRead, "%s%s", name, hobby);
}
}
fclose(pRead);
} //end readData
上面的代碼生成以下結(jié)果。
以下代碼使用goto和一些新功能(perror()和exit())來(lái)構(gòu)建文件I/O程序中的錯(cuò)誤處理。
#include <stdio.h>
#include <stdlib.h>
main() {
FILE *pRead;
char name[10];
char hobby[15];
pRead = fopen("hobbies.dat", "r");
if ( pRead == NULL )
goto ErrorHandler;
else {
printf("\nName\tHobby\n\n");
fscanf(pRead, "%s%s", name, hobby);
while ( !feof(pRead) ) {
printf("%s\t%s\n", name, hobby);
fscanf(pRead, "%s%s", name, hobby);
} //end loop
} // end if
exit(EXIT_SUCCESS); //exit program normally
ErrorHandler:
perror("The following error occurred");
exit(EXIT_FAILURE); //exit program with error
} //end main
exit()函數(shù)(<stdlib.h>庫(kù)的一部分)終止程序,就好像它正常退出一樣。
如下所示,exit()函數(shù)對(duì)于想要在遇到文件I/O(輸入/輸出)錯(cuò)誤時(shí)終止程序的程序員很常見。
exit(EXIT_SUCCESS); //exit program normally
或
exit(EXIT_FAILURE); //exit program with error
exit()函數(shù)接受一個(gè)參數(shù),一個(gè)常量為EXIT_SUCCESS或EXIT_FAILURE,兩者都分別為成功或失敗返回預(yù)定義的值。
perror()函數(shù)向標(biāo)準(zhǔn)輸出發(fā)送消息,描述遇到的最后一個(gè)錯(cuò)誤。
perror()函數(shù)采用單個(gè)字符串參數(shù),首先打印,后跟冒號(hào)和空白,然后系統(tǒng)生成錯(cuò)誤消息和新行。
perror("The following error occurred");
上面的代碼生成以下結(jié)果。
更多建議: