C語言文件操作入門指南
一、為什么使用文件
在學(xué)習(xí)完結(jié)構(gòu)體后,為了檢驗學(xué)習(xí)成果,我們寫了一個通訊錄的小程序,當(dāng)通訊錄運行起來的時候,可以給通訊錄中增加、刪除數(shù)據(jù),此時數(shù)據(jù)是存放在內(nèi)存中的,當(dāng)程序退出的時候,通訊錄中的數(shù)據(jù)就不存在了,等下次運行通訊錄程序的時候,數(shù)據(jù)又得重新錄入,如果使用這樣的通訊錄就很難受。
我們在想既然是通訊錄就應(yīng)該把信息記錄下來,只有我們自己選擇刪除數(shù)據(jù)的時候,數(shù)據(jù)才不復(fù)存在。
這就涉及到了數(shù)據(jù)持久化的問題,我們一般數(shù)據(jù)持久化的方法有,把數(shù)據(jù)存放在磁盤文件、存放到數(shù)據(jù)庫等方式。使用文件我們可以將數(shù)據(jù)直接存放在電腦的硬盤上,做到了數(shù)據(jù)的持久化。
二、什么是文件
- 磁盤上的文件是文件。
- 在程序設(shè)計中,我們一般談的文件有兩種:程序文件、數(shù)據(jù)文件(從文件功能的角度來分類的)。
2.1 程序文件
包括源程序文件(后綴為.c),目標(biāo)文件(windows環(huán)境后綴為.obj),可執(zhí)行程序(windows環(huán)境后綴為.exe)。
2.2 數(shù)據(jù)文件
文件的內(nèi)容不一定是程序,而是程序運行時讀寫的數(shù)據(jù),比如程序運行需要從中讀取數(shù)據(jù)的文件,或者輸出內(nèi)容的文件。
??在前面我們所處理數(shù)據(jù)的輸入輸出都是以終端為對象的,即從終端的鍵盤輸入數(shù)據(jù),運行結(jié)果顯示到顯示器上。
??其實有時候我們會把信息輸出到磁盤上,當(dāng)需要的時候再從磁盤上把數(shù)據(jù)讀取到內(nèi)存中使用,這里處理的就是磁盤上文件。
2.3 文件名
- 一個文件要有一個唯一的文件標(biāo)識,以便用戶識別和引用
- 文件名包含3部分:文件路徑+文件名主干+文件后綴
- 例如: c:\code\test.txt
三、文件的打開和關(guān)閉
3.1 文件指針
緩沖文件系統(tǒng)中,關(guān)鍵的概念是“文件類型指針”,簡稱“文件指針”。每個被使用的文件都在內(nèi)存中開辟了一個相應(yīng)的文件信息區(qū),用來存放文件的相關(guān)信息(如文件的名字,文件狀態(tài)及文件當(dāng)前的位置等)。這些信息是保存在一個結(jié)構(gòu)體變量中的。該結(jié)構(gòu)體類型是有系統(tǒng)聲明的,取名FILE。
??例如,VS2013編譯環(huán)境提供的 stdio.h 頭文件中有以下的文件類型申明:
struct _iobuf
{
char* _ptr;
int _cnt;
char* _base;
int _flag;
int _file;
int _charbuf;
int _bufsiz;
char* _tmpfname;
};
typedef struct _iobuf FILE;- 不同的C編譯器的FILE類型包含的內(nèi)容不完全相同,但是大同小異。
- 每當(dāng)打開一個文件的時候,系統(tǒng)會根據(jù)文件的情況自動創(chuàng)建一個FILE結(jié)構(gòu)的變量,并填充其中的信息,我們在使用時不必關(guān)心其細(xì)節(jié)。
- 一般都是通過一個FILE的指針來維護(hù)這個FILE結(jié)構(gòu)的變量,這樣使用起來更加方便。
?下面我們可以創(chuàng)建一個FILE*的指針變量:
FILE* pf;//文件指針變量
定義pf是一個指向FILE類型數(shù)據(jù)的指針變量。可以使pf指向某個文件的文件信息區(qū)(是一個結(jié)構(gòu)體變量)。通過該文件信息區(qū)中的信息就能夠訪問該文件。也就是說,通過文件指針變量能夠找到與它關(guān)聯(lián)的文件。
??比如:

3.2 文件的打開和關(guān)閉
文件在讀寫之前應(yīng)該先打開文件,在使用結(jié)束之后應(yīng)該關(guān)閉文件。
在編寫程序的時候,在打開文件的同時,都會返回一個FILE*的指針變量指向該文件,也相當(dāng)于建立了指針和文件的關(guān)系。
ANSIC 規(guī)定使用fopen函數(shù)來打開文件,fclose來關(guān)閉文件:
//打開文件 FILE * fopen ( const char * filename, const char * mode ); //關(guān)閉文件 int fclose ( FILE * stream );
??????打開方式如下:

??示例代碼:
#include <stdio.h>
int main()
{
//打開文件
FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//關(guān)閉文件
fclose(pf);
pf == NULL;
return 0;
}注意:我們平常所寫的程序保存數(shù)據(jù)是保存在內(nèi)存當(dāng)中的,而我們想把內(nèi)存當(dāng)中的數(shù)據(jù)放在文件當(dāng)中去,文件又是在硬盤上的,所以把內(nèi)存當(dāng)中的數(shù)據(jù)往硬盤上放的這個操作叫做寫文件或輸出操作,把文件當(dāng)中的數(shù)據(jù)往內(nèi)存里邊放的操作叫讀文件或輸入操作。
四、文件的順序讀寫

??深入理解 “流”:
- 在C語言中,流可以分為文件流和輸入輸出流。
- 流是一個高度抽象的概念,我們可以把它理解為信息流或者水流,在寫文件的過程中,會有很多的數(shù)據(jù),這些數(shù)據(jù)可能會傳輸?shù)讲煌牡胤饺?,比如說顯示到屏幕上,存到硬盤上,傳到網(wǎng)絡(luò)上等等,這些統(tǒng)稱為外部設(shè)備,不同的外部設(shè)備操作方式也不同。
- 要把數(shù)據(jù)傳到各種外部設(shè)備上去,就對程序員有較高的要求了,這時候就有人想把這個過程簡化一下,在外部設(shè)備和數(shù)據(jù)之間抽象一個東西,這個東西我們就叫做流,它里邊流淌的都是數(shù)據(jù),程序員現(xiàn)在只關(guān)心把數(shù)據(jù)怎么放在流里邊,至于流怎么把數(shù)據(jù)放在外部設(shè)備上去,那就不是我們要操心的事兒了,這樣一來,整個過程就簡單了許多。
- 回憶一下我們曾經(jīng)用scanf從鍵盤上讀取數(shù)據(jù),或用printf向屏幕上打印數(shù)據(jù),直接就操作了,好像沒有打開鍵盤或打開屏幕的操作,這是因為C語言程序只要運行起來,就會默認(rèn)打開三個流,分別為:標(biāo)準(zhǔn)輸入流 --- stdin、標(biāo)準(zhǔn)輸出流 --- stdout、標(biāo)準(zhǔn)錯誤流 --- stderr。因為scanf從鍵盤上讀取數(shù)據(jù)其實就是從標(biāo)準(zhǔn)輸入流里邊讀取數(shù)據(jù),而printf向屏幕上打印數(shù)據(jù)就是向標(biāo)準(zhǔn)輸出流里邊打印數(shù)據(jù),所以我們讀取數(shù)據(jù)或輸入數(shù)據(jù)的時候并沒有發(fā)現(xiàn)打開鍵盤或打開屏幕的操作。
- 文件流是用于文件讀寫操作的數(shù)據(jù)流,它可以從文件中讀取數(shù)據(jù),也可以向文件中寫入數(shù)據(jù),另外,文件流需要指定文件路徑和文件名,而標(biāo)準(zhǔn)輸出流不需要指定文件路徑,直接輸出到屏幕上。
??使用輸出流向屏幕輸出26個英文字母:
int main()
{
char ch = 0;
for (ch = 'a'; ch < 'z'; ch++)
{
if (ch % 5 == 0)
fputc('\n', stdout);
fputc(ch, stdout);
}
return 0;
}
??文件的順序讀寫函數(shù)介紹:
??fputc函數(shù):
1.函數(shù)原型:
int fputc ( int character, FILE * stream );
2.功能:
向指定的文件流中寫入一個字符。
3.示例:
int main()
{
//打開文件
FILE* pf = fopen("test.txt", "w");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//寫文件
fputc('a', pf);
fputc('b', pf);
fputc('c', pf);
fputc('d', pf);
//關(guān)閉文件
fclose(pf);
pf = NULL;
return 0;
}
在上述程序中,剛開始我們打開一個文件,它里邊什么都沒有 ,但是我有一個文件指針是指向這個文件的起始位置的(這兒所說的文件指針是指標(biāo)記字符位置的指針即光標(biāo),而不是pf);如果打開成功,接下來就要寫文件,最開始文件指針是指向第一個位置的,所以fputc把a寫了進(jìn)去,這時候文件指針的狀態(tài)就會更新,指向a的后邊,然后fputc再把b寫進(jìn)去,每一次進(jìn)行寫操作后,文件指針的位置就要發(fā)生變化,直到把所有的字符都寫進(jìn)去。然后fclose關(guān)閉文件,將文件保存起來。
??fgetc函數(shù):
1.函數(shù)原型:
int fgetc ( FILE * stream );
stream:要從中讀取字符的文件流。
2.功能:
從指定的文件流中讀取一個字符,并返回其ASCII值。
3.示例:


程序運行起來后,先打開文件,如果成功,就開始讀文件,讀文件的時候光標(biāo)默認(rèn)在最前面,當(dāng)fgetc讀一個字符的時候,光標(biāo)指向的那個位置為a,所以就返回字符a的ASCII值,然后打印在屏幕上,以同樣的步驟操作三次,就會將a、b、c分別打印在屏幕上,緊接著關(guān)閉文件。
??fputs函數(shù):
1.函數(shù)原型:
int fputs ( const char * str, FILE * stream );
str:要寫入文件的字符串。stream:要寫入的文件流。
2.功能:
向指定的文件流中寫入一行文本。
3.示例:
int main()
{
//打開文件
FILE* pf = fopen("test.txt", "w");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//寫文件
//寫到文件中去
fputs("hello\n", pf);
fputs("world!\n", pf);
//寫到屏幕上去
fputs("hello\n", stdout);
fputs("world!\n", stdout);
//關(guān)閉文件
fclose(pf);
pf = NULL;
return 0;
}

??fgets函數(shù):
1.函數(shù)原型:
char * fgets ( char * str, int num, FILE * stream );
str:指向用于存儲讀取字符串的字符數(shù)組的指針。num:要讀取的最大字符數(shù)(num - 1個)。stream:要從中讀取行的文件流。
2.功能:
從指定的文件流中讀取一行文本,并將其存儲到指定的字符串中。
3.示例:
int main(){//打開文件FILE* pf = fopen("test.txt", "r");if (pf == NULL){perror("fopen");return 1;}//讀文件char arr[10] = { 0 };fgets(arr, 3, pf);//關(guān)閉文件fclose(pf);pf = NULL;return 0;}

因為它讀取的最大字符數(shù)是num - 1個,所以只存儲了前兩個字符。
??fprintf函數(shù):
1.函數(shù)原型:
int fprintf ( FILE * stream, const char * format, ... );
stream:要寫入數(shù)據(jù)的文件流。format:格式字符串,指定了要寫入的數(shù)據(jù)的格式。...:要寫入數(shù)據(jù)的變量列表。
2.功能:
向指定的文件流中按照指定格式寫入數(shù)據(jù)。
3.示例:
struct S
{
float f;
char c;
int n;
};
int main()
{
struct S s = { 3.14f, 'w', 100 };
//打開文件
FILE* pf = fopen("test.txt", "w");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//寫文件
fprintf(pf, "%f %c %d", s.f, s.c, s.n);
//關(guān)閉文件
fclose(pf);
pf = NULL;
return 0;
}
??fscanf函數(shù):
1.函數(shù)原型:
int fscanf ( FILE * stream, const char * format, ... );stream:要從中讀取數(shù)據(jù)的文件流。format:格式字符串,指定了要讀取的數(shù)據(jù)的格式。...:要讀取的數(shù)據(jù)的變量列表。
2.功能:
從指定的文件流中按照指定的格式讀取數(shù)據(jù)。
3.示例:
struct S
{
float f;
char c;
int n;
};
int main()
{
struct S s = { 0 };
//打開文件
FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//寫文件
fscanf(pf, "%f %c %d", &(s.f), &(s.c), &(s.n));
printf("%f %c %d\n", s.f, s.c, s.n);
//關(guān)閉文件
fclose(pf);
pf = NULL;
return 0;
}
??fwrite函數(shù):
1.函數(shù)原型:
size_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream );
- ptr:指向要寫入的數(shù)據(jù)的緩沖區(qū)的指針。
- size:每個數(shù)據(jù)的字節(jié)數(shù)。
- count:要寫入的數(shù)據(jù)塊的數(shù)量。
- stream:要寫入數(shù)據(jù)的文件流。
2.功能:
向指定的文件流中寫入指定數(shù)量的數(shù)據(jù)塊。
3.示例:
//二進(jìn)制的方式寫進(jìn)文件
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
//打開文件
FILE* pf = fopen("text.txt", "wb");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//二進(jìn)制的寫文件
fwrite(arr, sizeof(arr[0]), sizeof(arr) / sizeof(arr[0]), pf);
//關(guān)閉文件
fclose(pf);
pf = NULL;
return 0;
}
可以看到寫進(jìn)去的值都變成了二進(jìn)制,我們看不懂,但是我們也可以以二進(jìn)制的方式讀取文件。
fread函數(shù):
1.函數(shù)原型:
size_t fread ( void * ptr, size_t size, size_t count, FILE * stream );
- ptr:指向存儲讀取數(shù)據(jù)的緩沖區(qū)的指針。
- size:每個數(shù)據(jù)的字節(jié)數(shù)。
- count:要讀取的數(shù)據(jù)塊的數(shù)量。
- stream:要讀取數(shù)據(jù)的文件流。
2.功能:
從指定的文件流中讀取指定數(shù)量的數(shù)據(jù)塊。
3.示例:
//二進(jìn)制的方式讀取文件
int main()
{
int arr[10] = { 0 };
//打開文件
FILE* pf = fopen("text.txt", "rb");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//二進(jìn)制的讀文件
fread(arr, sizeof(arr[0]), sizeof(arr) / sizeof(arr[0]), pf);
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", arr[i]);
}
//關(guān)閉文件
fclose(pf);
pf = NULL;
return 0;
}
??對比一組函數(shù):
scanf、fscanf、sscanf
printf、fprintf、sprintf
scanf是格式化的輸入函數(shù),針對的是標(biāo)準(zhǔn)輸入流 (鍵盤);printf是格式化的輸出函數(shù),針對的是標(biāo)準(zhǔn)輸出流(屏幕);綜上所述,scanf和printf是針對標(biāo)準(zhǔn)輸入/輸出流的格式化輸入/輸出函數(shù)。
fscanf是針對所有輸入流(文件流、標(biāo)準(zhǔn)輸入流)的格式化輸入函數(shù);sprintf是針對所有輸出流(文件流、標(biāo)準(zhǔn)輸出流)的格式化輸出函數(shù)。
sprintf是把格式化的數(shù)據(jù)轉(zhuǎn)化換成字符串;sscanf是將字符串轉(zhuǎn)換成格式化數(shù)據(jù)。
struct S
{
float f;
char c;
int n;
};
int main()
{
struct S s = { 3.14f, 'c', 100 };
char arr[100] = { 0 };
sprintf(arr, "%f %c %d", s.f, s.c, s.n);
printf("%s\n", arr);
struct S tmp = { 0 };
sscanf(arr, "%f %c %d", &(tmp.f), &(tmp.c), &(tmp.n));
printf("%f\n", tmp.f);
printf("%c\n", tmp.c);
printf("%d\n", tmp.n);
return 0;
} 
五、文件的隨機讀寫
5.1 fseek函數(shù):
1.函數(shù)原型:
int fseek ( FILE * stream, long int offset, int origin );
stream:指向FILE對象的指針,它標(biāo)識了要定位的文件。
offset:偏移量,即要移動的字節(jié)數(shù)??梢詾檎龜?shù)、負(fù)數(shù)或0,具體取決于origin參數(shù)。
origin:定位的起始位置,可以是下列常量之一:
SEEK_SET:從文件開頭開始偏移。SEEK_CUR:從當(dāng)前位置開始偏移。SEEK_END:從文件末尾開始偏移。
返回值:如果定位成功,fseek函數(shù)返回0;如果失敗,返回非零值。
2.功能:
fseek函數(shù)用于設(shè)置文件位置指針,以便在文件中進(jìn)行定位。它可以將文件位置指針設(shè)置到文件的任意位置,從而可以進(jìn)行讀取或?qū)懭氩僮鳌?/p>
3.示例:

int main()
{
//打開文件
FILE* pf = fopen("text.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 1;
}
int ch = fgetc(pf);
printf("%c\n", ch);//a
ch = fgetc(pf);
printf("%c\n", ch);//b
//ch = fgetc(pf);
//printf("%c\n", ch);//c
//fseek(pf, -2, SEEK_CUR);//從當(dāng)前位置開始偏移
//fseek(pf, 0, SEEK_SET);//從文件的開頭開始偏移
fseek(pf, -6, SEEK_END);//從文件的末尾開始偏移
ch = fgetc(pf);
printf("%c\n", ch);//a
//關(guān)閉文件
fclose(pf);
pf = NULL;
return 0;
} 
我的文件中放著一串字符串“abcdef”, 現(xiàn)在我想從中讀取字符出來,用fgetc函數(shù)就可以,但我想讓它第三個字符讀的是a,這個時候就可以用fseek函數(shù)。
5.2 ftell函數(shù):
1.函數(shù)原型:
long int ftell ( FILE * stream );stream:指向FILE對象的指針,用于標(biāo)識要獲取位置的文件。返回值:返回當(dāng)前位置相對于文件開頭的偏移量,如果出現(xiàn)錯誤則返回-1。
2. 功能:
用于獲取文件位置指針的當(dāng)前位置,即返回當(dāng)前位置相對于文件開頭的偏移量。
3.示例:
int main()
{
//打開文件
FILE* pf = fopen("text.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 1;
}
int ch = fgetc(pf);
printf("%c\n", ch);//a
ch = fgetc(pf);
printf("%c\n", ch);//b
ch = fgetc(pf);
printf("%c\n", ch);//c
int pos = ftell(pf);
printf("pos= %d\n", pos);
//關(guān)閉文件
fclose(pf);
pf = NULL;
return 0;
}
5.3 rewind函數(shù):
1. 函數(shù)原型:
void rewind ( FILE * stream ); stream:指向FILE對象的指針,用于標(biāo)識要重新定位的文件。
2.功能:
用于將文件位置指針重新定位到文件的開頭,即相當(dāng)于調(diào)用fseek(stream, 0, SEEK_SET)。
3.示例:
int main()
{
//打開文件
FILE* pf = fopen("text.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 1;
}
int ch = fgetc(pf);
printf("%c\n", ch);//a
ch = fgetc(pf);
printf("%c\n", ch);//b
//ch = fgetc(pf);
//printf("%c\n", ch);//c
rewind(pf);
ch = fgetc(pf);
printf("%c\n", ch);//a
//關(guān)閉文件
fclose(pf);
pf = NULL;
return 0;
}
六、文本文件和二進(jìn)制文件
- 根據(jù)數(shù)據(jù)的組織形式,數(shù)據(jù)文件被稱為文本文件或者二進(jìn)制文件。
- 數(shù)據(jù)在內(nèi)存中以二進(jìn)制的形式存儲,如果不加轉(zhuǎn)換的輸出到外存,就是二進(jìn)制文件。
- 如果要求在外存上以ASCII碼的形式存儲,則需要在存儲前轉(zhuǎn)換。以ASCII字符的形式存儲的文件就是文本文件。
那一個數(shù)據(jù)在內(nèi)存中是怎么存儲的呢?
字符一律以ASCII形式存儲,數(shù)值型數(shù)據(jù)既可以用ASCII形式存儲,也可以使用二進(jìn)制形式存儲。如有整數(shù)10000,如果以ASCII碼的形式輸出到磁盤,則磁盤中占用5個字節(jié)(每個字符一個字節(jié)),而以二進(jìn)制形式輸出,則在磁盤上只占4個字節(jié)(VS2013測試)。

??測試代碼:

我們將10000以 二進(jìn)制的形式寫到文件中,就是上述效果,我們自己是看不懂的,但VS卻能看懂,具體操作步驟如下圖:
int main()
{
int a = 10000;
FILE* pf = fopen("test.txt", "wb");
//二進(jìn)制的形式寫到文件中
fwrite(&a, 4, 1, pf);
fclose(pf);
pf = NULL;
return 0;
} 
將10000轉(zhuǎn)換成二進(jìn)制為 0010 0111 0001 0000,這是16個二進(jìn)制位,不夠32位,我們給它補齊0000 0000 0000 0000 0010 0111 0001 0000,每4個二進(jìn)制位轉(zhuǎn)換成1個16進(jìn)制位,就為0x00 0x00 0x27 0x10,那在內(nèi)存中以小端方式存放就為10 27 00 00。
七、文件讀取結(jié)束的判定
7.1 文本文件的讀取結(jié)束判定
文本文件讀取是否結(jié)束,判斷返回值是否為 EOF ( fgetc ),或者 NULL ( fgets )。fgetc 判斷是否為 EOF 。fgets 判斷返回值是否為 NULL。
ferror:在文件讀取結(jié)束后,用來判斷文件是否因為讀取過程中遇到錯誤而結(jié)束。feof:在文件讀取結(jié)束后,用來判斷文件是否因為讀取過程中遇到文件結(jié)束標(biāo)志而結(jié)束。牢記:在文件讀取過程中,不能用feof函數(shù)的返回值直接用來判斷文件是否結(jié)束。而是應(yīng)用于當(dāng)文件讀取結(jié)束的時候,判斷是讀取失敗結(jié)束,還是遇到文件尾結(jié)束 。
示例:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int c; // 注意:是int而非char,因為要求處理EOF,而EOF實際是-1,是個整型值
FILE* fp = fopen("test.txt", "r");
if (!fp)
{
perror("fopen");
return 1;
}
//fgetc 當(dāng)讀取失敗的時候或者遇到文件結(jié)束的時候,都會返回EOF
while ((c = fgetc(fp)) != EOF) // 標(biāo)準(zhǔn)C I/O讀取文件循環(huán)
{
putchar(c);
}
//判斷是什么原因結(jié)束的
if (ferror(fp))
puts("I/O error when reading");
else if (feof(fp))
puts("End of file reached successfully");
fclose(fp);
}
7.2 二進(jìn)制文件的讀取結(jié)束判定
fread函數(shù)判斷返回值是否小于實際要讀的個數(shù)。
size_t fread ( void * ptr, size_t size, size_t count, FILE * stream );fread要求讀取count個大小為size字節(jié)的數(shù)據(jù)。如果真的讀取到count個數(shù)據(jù),函數(shù)返回count。如果沒有讀取到count個數(shù)據(jù),返回的是真實讀取到的完整的數(shù)據(jù)個數(shù)。
示例:
#include <stdio.h>
enum { SIZE = 5 };
int main()
{
double a[SIZE] = { 1.,2.,3.,4.,5. };
FILE* fp = fopen("test.bin", "wb"); // 必須用二進(jìn)制模式
fwrite(a, sizeof * a, SIZE, fp); // 寫 double 的數(shù)組
fclose(fp);
double b[SIZE];
fp = fopen("test.bin", "rb");
size_t ret_code = fread(b, sizeof * b, SIZE, fp); // 讀 double 的數(shù)組
if (ret_code == SIZE)
{
puts("Array read successfully, contents: ");
for (int n = 0; n < SIZE; ++n)
printf("%f ", b[n]);
putchar('\n');
}
else
{ // error handling
if (feof(fp))
printf("Error reading test.bin: unexpected end of file\n");
else if (ferror(fp))
{
perror("Error reading test.bin");
}
}
fclose(fp);
return 0;
}
八、文件緩沖區(qū)
ANSIC 標(biāo)準(zhǔn)采用“緩沖文件系統(tǒng)”處理數(shù)據(jù)文件,所謂緩沖文件系統(tǒng)是指系統(tǒng)自動地在內(nèi)存中為程序中每一個正在使用的文件開辟一塊“文件緩沖區(qū)”。從內(nèi)存向磁盤輸出數(shù)據(jù)會先送到內(nèi)存中的緩沖區(qū),裝滿緩沖區(qū)后才一起送到磁盤上。如果從磁盤向計算機讀入數(shù)據(jù),則從磁盤文件中讀取數(shù)據(jù)輸入到內(nèi)存緩沖區(qū)(充滿緩沖區(qū)),然后再從緩沖區(qū)逐個地將數(shù)據(jù)送到程序數(shù)據(jù)區(qū)(程序變量等),緩沖區(qū)的大小根據(jù)C編譯系統(tǒng)決定。

#include <stdio.h>
#include <windows.h>
//VS2019 WIN10環(huán)境測試
int main()
{
FILE* pf = fopen("test.txt", "w");
fputs("abcdef", pf);//先將代碼放在輸出緩沖區(qū)
printf("睡眠10秒-已經(jīng)寫數(shù)據(jù)了,打開test.txt文件,發(fā)現(xiàn)文件沒有內(nèi)容\n");
Sleep(10000);
printf("刷新緩沖區(qū)\n");
fflush(pf);//刷新緩沖區(qū)時,才將輸出緩沖區(qū)的數(shù)據(jù)寫到文件(磁盤)
//注:fflush 在高版本的VS上不能使用了
printf("再睡眠10秒-此時,再次打開test.txt文件,文件有內(nèi)容了\n");
Sleep(10000);
fclose(pf);
//注:fclose在關(guān)閉文件的時候,也會刷新緩沖區(qū)
pf = NULL;
return 0;
}通過以上測試,可以得出一個結(jié)論:
因為有緩沖區(qū)的存在,C語言在操作文件的時候,需要做刷新緩沖區(qū)或者在文件操作結(jié)束的時候關(guān)閉文件。如果不做,可能導(dǎo)致讀寫文件的問題。
相關(guān)文章
C語言實現(xiàn)的統(tǒng)計素數(shù)并求和代碼分享
這篇文章主要介紹了C語言實現(xiàn)的統(tǒng)計素數(shù)并求和代碼分享,來自PAT平臺(浙江大學(xué)計算機程序設(shè)計能力考試系統(tǒng))的一個題目,需要的朋友可以參考下2014-08-08
Vscode搭建遠(yuǎn)程c開發(fā)環(huán)境的圖文教程
很久沒有寫C語言了,今天抽空學(xué)習(xí)下C語言知識,接下來通過本文給大家介紹Vscode搭建遠(yuǎn)程c開發(fā)環(huán)境的詳細(xì)步驟,本文通過圖文實例代碼相結(jié)合給大家介紹的非常詳細(xì),需要的朋友參考下吧2021-11-11

