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

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

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

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


程序運(yùn)行起來(lái)后,先打開(kāi)文件,如果成功,就開(kāi)始讀文件,讀文件的時(shí)候光標(biāo)默認(rèn)在最前面,當(dāng)fgetc讀一個(gè)字符的時(shí)候,光標(biāo)指向的那個(gè)位置為a,所以就返回字符a的ASCII值,然后打印在屏幕上,以同樣的步驟操作三次,就會(huì)將a、b、c分別打印在屏幕上,緊接著關(guān)閉文件。
??fputs函數(shù):
1.函數(shù)原型:
int fputs ( const char * str, FILE * stream );
str:要寫(xiě)入文件的字符串。stream:要寫(xiě)入的文件流。
2.功能:
向指定的文件流中寫(xiě)入一行文本。
3.示例:
int main()
{
//打開(kāi)文件
FILE* pf = fopen("test.txt", "w");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//寫(xiě)文件
//寫(xiě)到文件中去
fputs("hello\n", pf);
fputs("world!\n", pf);
//寫(xiě)到屏幕上去
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:指向用于存儲(chǔ)讀取字符串的字符數(shù)組的指針。num:要讀取的最大字符數(shù)(num - 1個(gè))。stream:要從中讀取行的文件流。
2.功能:
從指定的文件流中讀取一行文本,并將其存儲(chǔ)到指定的字符串中。
3.示例:
int main(){//打開(kāi)文件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;}

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

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

??測(cè)試代碼:

我們將10000以 二進(jìn)制的形式寫(xiě)到文件中,就是上述效果,我們自己是看不懂的,但VS卻能看懂,具體操作步驟如下圖:
int main()
{
int a = 10000;
FILE* pf = fopen("test.txt", "wb");
//二進(jìn)制的形式寫(xiě)到文件中
fwrite(&a, 4, 1, pf);
fclose(pf);
pf = NULL;
return 0;
} 
將10000轉(zhuǎn)換成二進(jìn)制為 0010 0111 0001 0000,這是16個(gè)二進(jìn)制位,不夠32位,我們給它補(bǔ)齊0000 0000 0000 0000 0010 0111 0001 0000,每4個(gè)二進(jìn)制位轉(zhuǎn)換成1個(gè)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é)束后,用來(lái)判斷文件是否因?yàn)樽x取過(guò)程中遇到錯(cuò)誤而結(jié)束。feof:在文件讀取結(jié)束后,用來(lái)判斷文件是否因?yàn)樽x取過(guò)程中遇到文件結(jié)束標(biāo)志而結(jié)束。牢記:在文件讀取過(guò)程中,不能用feof函數(shù)的返回值直接用來(lái)判斷文件是否結(jié)束。而是應(yīng)用于當(dāng)文件讀取結(jié)束的時(shí)候,判斷是讀取失敗結(jié)束,還是遇到文件尾結(jié)束 。
示例:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int c; // 注意:是int而非char,因?yàn)橐筇幚鞥OF,而EOF實(shí)際是-1,是個(gè)整型值
FILE* fp = fopen("test.txt", "r");
if (!fp)
{
perror("fopen");
return 1;
}
//fgetc 當(dāng)讀取失敗的時(shí)候或者遇到文件結(jié)束的時(shí)候,都會(huì)返回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í)際要讀的個(gè)數(shù)。
size_t fread ( void * ptr, size_t size, size_t count, FILE * stream );fread要求讀取count個(gè)大小為size字節(jié)的數(shù)據(jù)。如果真的讀取到count個(gè)數(shù)據(jù),函數(shù)返回count。如果沒(méi)有讀取到count個(gè)數(shù)據(jù),返回的是真實(shí)讀取到的完整的數(shù)據(jù)個(gè)數(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); // 寫(xiě) 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)自動(dòng)地在內(nèi)存中為程序中每一個(gè)正在使用的文件開(kāi)辟一塊“文件緩沖區(qū)”。從內(nèi)存向磁盤(pán)輸出數(shù)據(jù)會(huì)先送到內(nèi)存中的緩沖區(qū),裝滿緩沖區(qū)后才一起送到磁盤(pán)上。如果從磁盤(pán)向計(jì)算機(jī)讀入數(shù)據(jù),則從磁盤(pán)文件中讀取數(shù)據(jù)輸入到內(nèi)存緩沖區(qū)(充滿緩沖區(qū)),然后再?gòu)木彌_區(qū)逐個(gè)地將數(shù)據(jù)送到程序數(shù)據(jù)區(qū)(程序變量等),緩沖區(qū)的大小根據(jù)C編譯系統(tǒng)決定。

#include <stdio.h>
#include <windows.h>
//VS2019 WIN10環(huán)境測(cè)試
int main()
{
FILE* pf = fopen("test.txt", "w");
fputs("abcdef", pf);//先將代碼放在輸出緩沖區(qū)
printf("睡眠10秒-已經(jīng)寫(xiě)數(shù)據(jù)了,打開(kāi)test.txt文件,發(fā)現(xiàn)文件沒(méi)有內(nèi)容\n");
Sleep(10000);
printf("刷新緩沖區(qū)\n");
fflush(pf);//刷新緩沖區(qū)時(shí),才將輸出緩沖區(qū)的數(shù)據(jù)寫(xiě)到文件(磁盤(pán))
//注:fflush 在高版本的VS上不能使用了
printf("再睡眠10秒-此時(shí),再次打開(kāi)test.txt文件,文件有內(nèi)容了\n");
Sleep(10000);
fclose(pf);
//注:fclose在關(guān)閉文件的時(shí)候,也會(huì)刷新緩沖區(qū)
pf = NULL;
return 0;
}通過(guò)以上測(cè)試,可以得出一個(gè)結(jié)論:
因?yàn)橛芯彌_區(qū)的存在,C語(yǔ)言在操作文件的時(shí)候,需要做刷新緩沖區(qū)或者在文件操作結(jié)束的時(shí)候關(guān)閉文件。如果不做,可能導(dǎo)致讀寫(xiě)文件的問(wèn)題。
相關(guān)文章
C++實(shí)現(xiàn)病人就醫(yī)管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了C++語(yǔ)言實(shí)現(xiàn)病人就醫(yī)管理系統(tǒng),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-01-01
C語(yǔ)言 小游戲打磚塊實(shí)現(xiàn)流程詳解
打磚塊游戲是一種動(dòng)作電子游戲的名稱(chēng)。玩家操作一根螢?zāi)簧纤降摹鞍糇印保屢活w不斷彈來(lái)彈去的“球”在撞擊作為過(guò)關(guān)目標(biāo)消去的“磚塊”的途中不會(huì)落到螢?zāi)坏紫隆G蚺龅酱u塊、棒子與底下以外的三邊會(huì)反彈,落到底下會(huì)失去一顆球,把磚塊全部消去就可以破關(guān)2021-11-11
C語(yǔ)言實(shí)現(xiàn)的統(tǒng)計(jì)素?cái)?shù)并求和代碼分享
這篇文章主要介紹了C語(yǔ)言實(shí)現(xiàn)的統(tǒng)計(jì)素?cái)?shù)并求和代碼分享,來(lái)自PAT平臺(tái)(浙江大學(xué)計(jì)算機(jī)程序設(shè)計(jì)能力考試系統(tǒng))的一個(gè)題目,需要的朋友可以參考下2014-08-08
Vscode搭建遠(yuǎn)程c開(kāi)發(fā)環(huán)境的圖文教程
很久沒(méi)有寫(xiě)C語(yǔ)言了,今天抽空學(xué)習(xí)下C語(yǔ)言知識(shí),接下來(lái)通過(guò)本文給大家介紹Vscode搭建遠(yuǎn)程c開(kāi)發(fā)環(huán)境的詳細(xì)步驟,本文通過(guò)圖文實(shí)例代碼相結(jié)合給大家介紹的非常詳細(xì),需要的朋友參考下吧2021-11-11
C++實(shí)現(xiàn)簡(jiǎn)單的HTTP服務(wù)器
這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)簡(jiǎn)單的HTTP服務(wù)器的相關(guān)資料,感興趣的朋友可以參考下2016-05-05
C語(yǔ)言簡(jiǎn)易版flappy bird小游戲
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言簡(jiǎn)易版flappy bird小游戲,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-12-12
C++?STL容器詳解之紅黑樹(shù)部分模擬實(shí)現(xiàn)
本文主要對(duì)紅黑樹(shù)進(jìn)行了詳細(xì)介紹,并對(duì)其核心功能進(jìn)行了模擬實(shí)現(xiàn)。文中的代碼對(duì)我們的學(xué)習(xí)或工作有一定的價(jià)值,感興趣的小伙伴可以了解一下2021-12-12

