
目錄
- 什么是檔案
- 檔案名
- 檔案型別
- 檔案緩沖區
- 檔案指標
- 總結:
- 補充:
- 檔案的打開和關閉
- 那么如何打開桌面上的檔案呢?
- 檔案的順序讀寫
- 檔案讀寫的函式
- 字符輸出函式fputc
- 流的概念
- 字符輸入函式fgetc
- 總結:
- 文本行輸出函式 fputs
- 文本行輸入函式 fgets
- 格式化輸出函式 fprintf
- 格式化輸入函式 fscanf
- 二進制輸出 fwrite
- 二進制輸入 fread
- sprintf
- sscanf
- 檔案的隨機讀寫
- fseek
- ftell
- rewind
- 文本檔案和二進制檔案
- 總結:
- 關于二進制檔案和文本檔案的區別
- 檔案結束的判定
- 被錯誤使用的 feof
- 總結:
- 檔案緩沖區
- 驗證緩沖區的存在
什么是檔案
-
什么是檔案 磁盤上的檔案是檔案,
-
但是在程式設計中,我們一般談的檔案有兩種:程式檔案、資料檔案
-
程式檔案
包括源程式檔案(后綴為.c),目標檔案(windows環境后綴為.obj),可執行程式(windows環境后綴 為.exe), -
資料檔案 檔案的內容不一定是程式,而是程式運行時讀寫的資料,比如程式運行需要從中讀取資料的檔案,或者輸出內 容的檔案,

本章只討論資料檔案,資料檔案總結一下:就是你可以用程式去向檔案中(輸出資訊 / 寫)又或者從檔案中(輸入資訊 /讀)到你的程式中去,而在這里需要站在記憶體的角度想清楚檔案的讀和寫
- 在以前各章所處理資料的輸入輸出都是以終端為物件的,即從終端的鍵盤輸入資料,運行結果顯示到顯示幕上,
- 其實有時候我們會把資訊輸出到磁盤上,當需要的時候再從磁盤上把資料讀取到記憶體中使用,這里處理的就是磁盤上的檔案,所以我們今天面對的輸入輸出物件就是檔案
檔案名
- 檔案名包含3部分:檔案路徑+檔案名主干+檔案后綴
- 例如: c:\code\test.txt
- 為了方便起見,檔案標識常被稱為檔案名
檔案型別
檔案緩沖區
檔案指標
-
緩沖檔案系統中,關鍵的概念是“檔案型別指標”,簡稱“檔案指標”,
-
每個被使用的檔案都在記憶體中開辟了一個相應的檔案資訊區,用來存放檔案的相關資訊(如檔案的名字,檔案狀態及
檔案當前的位置等), -
這些資訊是保存在一個結構體變數中的,
-
該結構體型別是有系統宣告的,取名FILE. 例如,VS2008編譯環境提供的stdio.h 頭檔案中有以下的檔案型別申明:


總結:
當檔案被打開的時候會自動創建一個檔案資訊區,檔案資訊區是強關聯該檔案的,他會記錄檔案資訊實際上他就是一個結構體變數,struct _iobuf 是檔案的本質型別,FILE是由typedef定義出來的檔案型別,他也是一個檔案型別,
而檔案資訊區的型別是一個struct FILE型別
補充:
-
不同的C編譯器的FILE型別包含的內容不完全相同,但是大同小異,
每當打開一個檔案的時候,系統會根據檔案的情況自動創建一個FILE結構的變數,并填充其中的資訊,使用者不必關 心細節,
一般都是通過一個FILE的指標來維護這個FILE結構的變數,這樣使用起來更加方便 -
定義pf是一個指向FILE型別資料的指標變數,可以使pf指向某個檔案的檔案資訊區(是一個結構體變數),
-
通過該檔案資訊區中的資訊就能夠訪問該檔案,也就是說,通過檔案指標變數能夠找到與它關聯的檔案
//以只讀的方式打開data檔案,并回傳該檔案資訊區的起始地址
FILE *pf1 = fopen("data1.txt,"r"");
FILE *pf2 = fopen("data2.txt,"r"");
FILE *pf3 = fopen("data3.txt,"r"");
如圖所示是檔案指標與檔案資訊區對應的關系

總結:檔案資訊區實際上是一個FILE型別的結構體變數,關于他是怎么創建的,其實是在檔案被打開的時候就會自動生成,并且是關聯這個檔案的,最后再回傳檔案資訊區的起始地址,有了檔案資訊區的起始地址就能找到檔案
檔案的打開和關閉
注意事項:
- 1、檔案在讀寫之前應該先打開檔案,
- 2、在使用結束之后應該關閉檔案,
- 3、檔案在撰寫程式的時候,在打開檔案的同時,都會回傳一個FILE*的指標變數指向該檔案,也相當于建立了指標和檔案的關系
- 4、ANSIC 規定使用fopen函式來打開檔案,fclose來關閉檔案
檔案打開函式的原型
FILE * fopen ( const char * filename, const char * mode );
filename:是打開檔案名
mode 是以什么模式打開
檔案關閉函式的原型
int fclose ( FILE * stream );
stream :傳入一個檔案指標
打開方式如下:

//函式功能打開一個檔案
void filetest()
{
//以只讀的方式“r”打開檔案回傳檔案資訊區的起始地址
FILE *pf = fopen("data.txt","r");
//如果打開失敗回傳NULL,就列印錯誤資訊并退出程式
if(pf == NULL)
{
perror("file");
exit(-1);
}
//讀檔案
//關閉檔案,有點類似于free函式的寫法
flcose(pf);
//指標置空防止成為野指標
pf = NULL
}
int main()
{
filetest();
return 0;
}
以只讀的方式“r”打開 由于此時在我的工程中并沒有data這個資料檔案,所以會列印錯誤資訊提示我沒有此檔案,說明我們的檔案打開失敗了

既然沒有這個檔案那么就創建一個檔案

當我們的工程目錄下多出了一個data.txt會是什么樣呢

這個時候程式正常,并沒有出錯,
總結一下:
當以不指定路徑的方式打開檔案,默認先訪問工程目錄下,如果工程下找不到這個檔案又以只讀的方式 “r” 打開檔案那么就會出錯,
那么如何打開桌面上的檔案呢?
其實桌面上的檔案和從磁盤上打開檔案并沒有多大的區別,因為fopen的第一個引數只需要接受一個檔案名(檔案路徑+檔案名主干+檔案后綴),和使用打開方式就行

這時在我的桌面上新建一個文本檔案(資料檔案),我們在程式中將他打開,先來觀察這個檔案所處的路徑

有了檔案的路徑就只需要將檔案路徑傳給fopen函式就行,而在這需要注意的是 \ 遇到字符會變成轉義字符,而為了防止他成為轉義字符就需要用雙斜杠將 / 轉義成普通的斜杠,這樣才是一個完整的路徑

void filetest()
{
FILE *pf = fopen("C:\\Users\\26961\\Desktop\\data.txt","r");
if(pf == NULL)
{
perror("file");
exit(-1);
}
fclose(pf);
pf = NULL;
}
int main()
{
filetest();
return 0;
}
那么關于以只讀 “r” 的方式打開檔案的舉例已經夠了,再來看看以“w"(只寫)的方式打開檔案會是怎么樣的呢,這里需要注意細節

可以看到如果桌面上沒有這個檔案,那么以 “ w ” 只寫的方式打開檔案會創建一個檔案,我們再來看如果檔案已經存在了再以“ w ”只讀的方式打開會怎么樣

很明顯可以看出之前的資訊明顯是保存了,可是再次打開這個檔案中卻什么都沒有,看到這里其實我們也應該直到這個fopen函式的脾氣了,當檔案不存在就會創建一個檔案,而當檔案存在就會銷毀該檔案的內容
檔案的順序讀寫
當我們的程式需要寫入一個資料到檔案當中去的時候對應的動作是(輸出/寫)這里用fputc表示,當我們從檔案當中讀取一個資料到記憶體中去的時候對應的動作是(輸入/讀),這里用fgetc表示,

檔案讀寫的函式

字符輸出函式fputc
int fputc( int c, FILE *stream );
c:要寫入到檔案的字符
stream :FILE結構的指標
int fputc( int c, FILE *stream )函式的使用,會將記憶體中的資料寫入到檔案中去,
void filetest()
{
FILE *pf = fopen("C:\\Users\\26961\\Desktop\\data.txt","w");
if(pf == NULL)
{
perror("file");
exit(-1);
}
//順序寫入檔案,將字符寫到pf關聯的檔案中
fputc('a',pf);
fputc('b', pf);
fputc('c', pf);
fputc('d', pf);
//關閉檔案
fclose(pf);
pf = NULL;
}
當程式成功執行起來的時候,可以看到文本檔案已經存放了abcd四個字符

那么有沒有讀者會比較好奇fputc是怎么將字符寫入到檔案中的?
其實pf這個資訊區里面存放了一個指標用來記錄存取字符的位置,當存入字符后,指標會往后偏移,這樣子一點一點地就將資料存放完了,
流的概念
fputc他是適用于所有輸出流的(檔案 / 標準輸出),在我們c語言中有一個流的概念,流相當于水流,他是一種高度抽象的概念,而一些外部設備比如:檔案、螢屏、網路、光碟、軟盤,我可以從一些外部設備中讀寫一些資料,而不同外部設備的讀寫方式肯定不一樣,而作為程式員不需要關心怎么向外部設備讀寫資料,就在這些外部設備該需要采用如何讀寫的方式這個問題交給誰的時候,c語言封裝了流,程式員只需要關心如何往流里面輸入資料,并不需要關心資料給誰,資料給誰都是流來考慮

- c語言的程式只要一運行起來就會默認打開3個流:
- 標準輸出流 - stdout
- 標準輸入流 - stdin
- 標準錯誤流 - stderr
- 這3個流的型別都是FILE型別
fputc適用于所有輸出流,這里再舉例標準輸出流(螢屏)
void fpunc()
{ //將字符順序輸出到螢屏
fputc('a',stdout);
fputc('b', stdout);
fputc('c', stdout);
fputc('d', stdout);
}
列印結果

字符輸入函式fgetc
// 適用于所有輸入流
int fgetc( FILE *stream );
// stream:FILE結構的指標
fgetc適用于所有輸入流,這個函式功能是可以從一個流里面讀取資料然后回傳,當然這里是從檔案流里面讀取資料
void filetest()
{
FILE *pf = fopen("C:\\Users\\26961\\Desktop\\data.txt","r");
if(pf == NULL)
{
perror("file");
exit(-1);
}
//從檔案流中讀取資料回傳存放到變數ch中
int ch = fgetc(pf);
printf("%c",ch);
ch = fgetc(pf);
printf("%c", ch);
ch = fgetc(pf);
printf("%c", ch);
ch = fgetc(pf);
printf("%c", ch);
//關閉檔案
fclose(pf);
pf = NULL;
}
列印結果

其實pf這個資訊區里面存放了一個指標用來記錄存取字符的位置,當取出一個字符后,指標會往后偏移,這樣子一點一點地就將資料依次取出來了
fgetc函式讀取標準輸入流(鍵盤)
void pfunc()
{
//從標準輸入流讀取字符存放到變數ch中
int ch = fgetc(stdin);
printf("%c\n",ch);
ch = fgetc(stdin);
printf("%c\n", ch);
ch = fgetc(stdin);
printf("%c\n", ch);
ch = fgetc(stdin);
printf("%c\n", ch);
}
列印結果

總結:
- 1、fgetc()是適用于所有的輸入流 ,它可以用于讀入所有輸入流
- 2、fputc()函式是適用于所有的輸入流,它可以用于輸出所有輸出流
文本行輸出函式 fputs
功能:將一個字串輸出到檔案中
函式原型:
int fputs( const char *string, FILE *stream );
string : 想要輸出的字串
stream : FILE*型別的結構體指標
void filetest()
{
FILE *pf = fopen("C:\\Users\\26961\\Desktop\\data.txt","w");
if(pf == NULL)
{
perror("file");
exit(-1);
}
//寫檔案,將字串寫到pf關聯的檔案中
fputs("男人喜歡吃米飯\n",pf);
fputs("女人喜歡吃面食\n", pf);
//關閉檔案
fclose(pf);
pf = NULL;
}
程式運行后的結果:
可以看到字串成功地寫入進了檔案中去,這個函式注意的地方是如果你想讓他換行就必須再字串中加入‘\n’

文本行輸入函式 fgets
這個函式的功能是從流中讀取一個字符存放到一個字符指標中去,前提是字符指標指向的空間夠大
char *fgets( char *string, int n, FILE *stream );
string : 資料存盤位置
n :可讀的最大字符數
stream :FILE結構的指標
文本行輸入函式 fgets的使用以及注意事項
void filetest()
{
FILE *pf = fopen("C:\\Users\\26961\\Desktop\\data.txt","r");
if(pf == NULL)
{
perror("file");
exit(-1);
}
//寫檔案,將字串寫到pf關聯的檔案中
char arr[20] = { 0 };
fgets(arr,5,pf);
printf("%s", arr);
fgets( arr,5,pf);
printf("%s",arr);
//關閉檔案
fclose(pf);
pf = NULL;
}
從程式運行的結果不妨猜測一下為什么,結果會是這樣子

其實fgets這個函式的引數n可讀的最大字符數雖然是5,以為可以讀5個字符,實際上只能讀到n - 1個字符,也就是4個字符,并且末尾會補‘\0’保證有5個字符
那么如果是讀取n = 20個他會全部讀完嗎?
void filetest()
{
FILE *pf = fopen("C:\\Users\\26961\\Desktop\\data.txt","r");
if(pf == NULL)
{
perror("file");
exit(-1);
}
//已將讀取的長度改為20
char arr[20] = { 0 };
fgets(arr,20,pf);
printf("%s\n", arr);
fgets( arr,20,pf);
printf("%s\n",arr);
//關閉檔案
fclose(pf);
pf = NULL;
}
從列印的結果可以看出不管n指定有多大,fgets只會讀取一行資料,并不會為了填充這20個大小的目標而去讀下一行字串,

格式化輸出函式 fprintf
函式功能:以指定的格式將資料輸出到流里面去
fprintf函式的原型:
int fprintf( FILE *stream, const char *format [, argument ]...);
stream:FILE結構的指標
printf函式的原型和fprintf函式的原型對比:

我們可以看出fprintf和printf的函式原型好像基本是一樣的,那么它們的使用是不是也基本一樣呢?下面來試試看
void filetest()
{
FILE *pf = fopen("C:\\Users\\26961\\Desktop\\data.txt","w");
if(pf == NULL)
{
perror("file");
exit(-1);
}
//寫檔案,將資料寫到pf關聯的檔案中
int arr[10] = { 0 };
for (int i = 0; i < 10; i++)
{
fprintf(pf, "%d\n", arr[i]++);
}
//關閉檔案
fclose(pf);
pf = NULL;
}
程式執行完的結果:

我們可以看出好像fprintf和printf的使用上沒太大的區別,只是一個是輸出到螢屏上去的,而另外一個是輸出到檔案上去的
格式化輸入函式 fscanf
函式功能:以指定的格式讀取出流里面的資料放到記憶體中去
fscanf函式的原型:
int fscanf( FILE *stream, const char *format [, argument ]... );
可以看出fscanf和scanf函式原型上其實也差不多,在這里就直接對標了哈哈哈

代碼:
void filetest()
{
FILE *pf = fopen("C:\\Users\\26961\\Desktop\\data.txt","r");
if(pf == NULL)
{
perror("file");
exit(-1);
}
//讀檔案,從檔案流中拿去資料存放到a和b變數中
int a = 0, b = 0;
fscanf(pf,"%d %d", &a, &b);
printf("%d %d", a, b);
//關閉檔案
fclose(pf);
pf = NULL;
}
原本就存放在檔案中的資料:

列印的結果:

二進制輸出 fwrite
功能:以二進制的方式將程式中的資料輸出到檔案中
size_t fwrite( const void *buffer, size_t size, size_t count, FILE *stream );
buffer:指向要寫入資料的指標
size:元素大小(以位元組為單位)
count:要寫的元素的最大個數
stream :FILE結構的指標
代碼
struct s
{
int a;
int b;
char name[20];
};
void filetest()
{
//"wb":為了輸出資料,打開一個二進制檔案
FILE *pf = fopen("C:\\Users\\26961\\Desktop\\data.txt", "wb");
if(pf == NULL)
{
perror("file");
exit(-1);
}
//寫檔案,將資料寫到pf關聯的檔案中
struct s s = { 0,40 ,"zhangsan"};
fwrite(&s,sizeof(struct s),1,pf);
//關閉檔案
fclose(pf);
pf = NULL;
}
程式運行的實際效果,這里我們可以看到這樣zhangsan的拼音是我們看的懂得,因為字串本身就是以文本形式存放,而剩下得資料我們就看不懂了,因為是以二進制的形式寫進檔案里去的,二進制的形式寫進去的跟以文本寫進去的會有很大區別

既然以看不懂二進制的表示方式,那我們就用程式將這個文本檔案的資訊讀取出來,“rb”(只讀) 為了輸入資料,打開一個二進制檔案,二進制輸入,fread登場
二進制輸入 fread
功能:從一個流里面讀取count個大小為size(單位位元組)的資料存放到buffer里去
size_t fread( void *buffer, size_t size, size_t count, FILE *stream );
buffer:資料存盤位置
size:元素大小(以位元組為單位)
count:要讀取的最大的元素個數
stream :FILE結構的指標

Fread回傳實際讀取的完整項的數量,如果發生錯誤或在達到count之前遇到檔案結束,則可能小于count,使用feof或ferror函式來區分讀錯誤和檔案結束條件,如果size或count為0,fread回傳0并且緩沖區內容不變,

對標一下我們可以發現,其實它們之間的差別就是一個加了const和沒加const的區別,可實際上卻并不是這點差別
代碼:
void filetest()
{
struct s s = { 0 };
//"rb" 為了輸入資料,打開一個二進制檔案
FILE *pf = fopen("C:\\Users\\26961\\Desktop\\data.txt", "rb");
if(pf == NULL)
{
perror("file");
exit(-1);
}
//讀檔案,打開一個二進制的檔案
fread(&s,sizeof(struct s),1,pf);
printf("%d %d %s",s.a,s.b,s.name);
//關閉檔案
fclose(pf);
pf = NULL;
}
我們最先開始存放進去的是這個資料,顯示在文本中后看到的效果又是這樣

程式運行:

從程式運行的結果來看,確確實實地是將檔案中的資料以二進制的方式輸入到記憶體中去了,就像是給結構體重新賦值了一樣
對比一組函式:
scanf/fscanf/sscanf
printf/fprintf/sprintf
結論:

sprintf

函式功能:是將一個格式化的資料轉換成一個字串
代碼:
void pfunc()
{
char arr[20] = { 0 };
struct s s = {10,100,"zhangsan"};
//將一個格式化的資料轉換成字串,在存放到陣列中去
sprintf(arr, "%d %d %s", s.a, s.b, s.name);
//輸出陣列中的內容到螢屏上去
printf("%s\n",arr);
}
程式運行結果:

sscanf

函式功能:是從arr的字串中提取出一個格式化的資料
代碼:
void pfunc()
{
char arr[20] = { 0 };
struct s s = {10,100,"zhangsan"};
struct s tmp = { 0 };
//將一個格式化的資料轉換成字串,存放到arr中
sprintf(arr, "%d %d %s", s.a, s.b, s.name);
//將arr字串的中提取出一個格式化的資料,存放到tmp結構體中
sscanf(arr,"%d %d %s",&tmp.a, &tmp.b, tmp.name );
//列印結構體的資料
printf("%d %d %s\n",tmp.a,tmp.b,tmp.name);
}
運行結果:

檔案的隨機讀寫

fseek
- 將檔案指標移動到指定位置,
- 根據檔案指標的位置和偏移量來定位檔案指標
int fseek( FILE *stream, long offset, int origin );
stream:FILE結構的指標
offset:從原點開始的位元組數
origin:初始位置
SEEK_CUR
Current position of file pointer – 檔案指標的當前位置
SEEK_END
End of file — 檔案終點
SEEK_SET
Beginning of file – 檔案起始位置
如果檔案指標指向的是中間位置c的話,那么從c的位置都是從0開始遞增d字符是偏移量為1,e是檔案指標的偏移量為2,e是檔案指標的偏移量3,(右邊為正數),而他的左邊b是檔案指標的偏移量為-1的位置,a是檔案指標偏移量為-2的位置

如果檔案指標指向的是起始位置a的地方,那么從a的位置處開始往后偏移都是正數

fseek 的使用代碼
void filetest()
{
//"rb" 為了輸入資料,打開一個二進制檔案
FILE *pf = fopen("C:\\Users\\26961\\Desktop\\data.txt", "r");
if(pf == NULL)
{
perror("file");
exit(-1);
}
//檔案打開成功,從檔案指標的起始位置往后偏移2個位元組數,讀完一個字符檔案指標還會向后偏移一位元組
fseek(pf,2,SEEK_SET);
int ch = fgetc(pf);
printf("%c\n", ch);
//如果想往回再讀b,可以從當前位置向前偏移-2個位元組
fseek(pf,-2,SEEK_CUR);
ch = fgetc(pf);
printf("%c\n", ch);
//關閉檔案
fclose(pf);
pf = NULL;
}
從運行結果結果可以,fseek 確實是具有移動檔案指標的能力的,配合著fgetc函式使用,基本就是指哪打哪,即使是操作檔案也搓搓有余

ftell
計算檔案指標相對于起始位置的偏移量函式
long ftell( FILE *stream );
函式功能:獲取檔案指標的當前位置,

rewind
讓檔案指標的位置回到檔案的起始位置
void rewind ( FILE * stream );
上個例子我們已經看到了ftell他就像一個追蹤器一樣跟著檔案指標,檔案指標走哪他也走哪,那么接下來再介紹一個函式,比較霸氣的函式rewind,媽媽讓你回家吃飯你就回家吃飯檔案指標別亂跑

從程式的運行結果可以看出檔案指標還是一個好孩子,已經回到了偏移量為0的起始位置,哈哈
文本檔案和二進制檔案
-
根據資料的組織形式,資料檔案被稱為文本檔案或者二進制檔案,
-
資料在記憶體中以二進制的形式存盤,如果不加轉換的輸出到外存,就是二進制檔案, .
-
如果要求在外存上以ASCII碼的形式存盤,則需要在存盤前轉換,以ASCII字符的形式存盤的檔案就是文本檔案,
-
一些二進制檔案比如exe,一些資料檔案比如txt
總結:
- 1、二進制檔案就是在記憶體中以二進制形式存盤的資料不加轉換地輸出到外存(檔案)
- 2、文本檔案要求在外存上以ASCII碼的形式存盤,則需要在存盤前轉換,轉換之后的資料輸出到外存上(檔案),
關于二進制檔案和文本檔案的區別
- 一個資料在記憶體中是怎么存盤的呢? 字符一律以ASCII形式存盤,數值型資料既可以用ASCII形式存盤,也可以使用二進制形式存盤,
- 如有整數10000,如果以ASCII碼的形式輸出到磁盤,則磁盤中占用5個位元組(每個字符一個位元組),而二進制形式輸出,則在磁盤上只占4個位元組(VS2013測驗)
測驗代碼:
#include <stdio.h>
int main()
{
int a = 10000;
FILE* pf = fopen("test.txt", "wb");
fwrite(&a, 4, 1, pf);//二進制的形式將資料寫入檔案中
fclose(pf);
pf = NULL;
return 0; }
那么10000是怎么以二進制的形式存放到記憶體中去的呢
10000的二進制序列:00000000 00000000 00100111 00010000

10000的二進制序列轉化為16進制是 00 00 27 10

當我們將程式運行后把這個二進制檔案一打開確實看見了10000對應的16進制,由于在vs2017是小端存盤所以低地址處存放低地址處資料,高地址處存放高地址處資料,所以看到的是反過來的

檔案結束的判定

- 文本檔案讀取是否結束,判斷回傳值是否為EOF (fgetc),或者NULL(fgets)
- 例如: fgetc判斷是否為EOF.
- fgets判斷回傳值是否為NULL.
2.二進制檔案的讀取結束判斷,判斷回傳值是否小于實際要讀的個數, 例如: fread / fscanf 判斷回傳值是否小于實際要讀的個數
被錯誤使用的 feof
int feof( FILE *stream );
- 牢記:在檔案讀取程序中,不能用feof函式的回傳值直接用來判斷檔案的是否結束,
- 而是應用于當檔案讀取結束的時候,判斷是讀取失敗結束,還是遇到檔案尾結束
比如fgets是讀到檔案末尾了才結束,那么如果中途就停止讀檔案了,就可以使用feof 函式來判斷他是遇到檔案末尾了,還是中途讀取失敗
來看feof的使用
int c; // 注意:int,非char,要求處理EOF
FILE* fp = fopen("test.txt", "r");
if(!fp) {
perror("File opening failed");
return EXIT_FAILURE;
}
//fgetc 當讀取失敗的時候或者遇到檔案結束的時候,都會回傳EOF
while ((c = fgetc(fp)) != EOF) // 標準C I/O讀取檔案回圈
{
putchar(c);
}
//判斷是什么原因結束的
//如果檔案讀取失敗就會回傳錯誤資訊
if (ferror(fp))
puts("I/O error when reading");
//feof判斷檔案是否成功讀到末尾
else if (feof(fp))
puts("End of file reached successfully");
fclose(fp);
總結:
feof的用途:是檔案讀取結束了,判斷是不是遇到檔案末尾而結束的
ferror的用途:檔案讀取結束了,判斷是不是遇到錯誤后讀取結束
檔案緩沖區
-
ANSIC 標準采用“緩沖檔案系統”處理的資料檔案的,所謂緩沖檔案系統是指系統自動地在記憶體中為程式中每一個正在使用的檔案開辟一塊“檔案緩沖區”,
-
從記憶體向磁盤輸出資料會先送到記憶體中的緩沖區,裝滿緩沖區后才一起送到磁盤上,
-
如果從磁盤向計算機讀入資料,則從磁盤檔案中讀取資料輸入到記憶體緩沖區(充滿緩沖區),然后再從緩沖區逐個地將資料送到程式資料區(程式變數等),
-
緩沖區的大小根據C編譯系統決定的,

驗證緩沖區的存在
void pfunc()
{
FILE* pf = fopen("test.txt", "w");
if (!pf)
{
perror("fopen:");
exit(-1);
}
fputs("abcdef",pf);//先將資料放在輸出緩沖區
printf("已經休眠10秒鐘 -- 打開test.txt檔案,發現檔案沒有內容\n");
Sleep(10000);
printf("重繪緩沖區\n");
//函式重繪緩沖區時,才將緩沖區的資料寫到檔案中去,而fflush函式就有重繪緩沖區的作用
fflush(pf);
printf("再休眠10秒鐘 -- 打開test.txt檔案,發現檔案有內容\n");
//fclose函式再關閉檔案時也會重繪緩沖區
fclose(pf);
pf = NULL;
}

fputs(“abcdef”,pf);代碼只是將資料輸出到了緩沖區中,等待10秒后當緩沖區被重繪的時候,才會將緩沖區的資料存放到檔案中
這里還可以得到結論:
因為有緩沖區的存在,c語言操作檔案的時候,需要重繪緩沖區或者在檔案結束的時候關閉檔案,如果不做,可能會無法保存
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/304374.html
標籤:其他
上一篇:線下精準大資料獲客
下一篇:【歷史上的今天】9 月 29 日:“美國支付寶” Stripe 正式上線;HotJava 面世;VR/AR 領域先驅誕生
