柚子快報(bào)激活碼778899分享:開發(fā)語言 C語言之旅:文件操作
柚子快報(bào)激活碼778899分享:開發(fā)語言 C語言之旅:文件操作
目錄
一什么是文件
1.1程序文件:
1.2數(shù)據(jù)文件:
1.3文件名
二.文件的打開與關(guān)閉
2.1流和標(biāo)準(zhǔn)流
2.2 文件指針
?2.3文件的打開與關(guān)閉
?三.文件的順序讀寫
3.1順序讀寫函數(shù)介紹
fgetc
fputc
fgets
fputs
四.文件的隨機(jī)讀寫
4.1 fseek
4.2 ftell
4.3 rewind
五.文件讀取結(jié)束的判定
文件緩沖區(qū)
一什么是文件
文件是數(shù)據(jù)按特定格式或協(xié)議存儲(chǔ)在外部介質(zhì)上的一種數(shù)據(jù)集合。文件通常是存儲(chǔ)在計(jì)算機(jī)存儲(chǔ)介質(zhì)上,如硬盤、光盤、磁帶等,也可以是存儲(chǔ)在外部設(shè)備或網(wǎng)絡(luò)中。
但是在程序設(shè)計(jì)中,我們?般談的文件件有兩種:程序文件、數(shù)據(jù)文件(從文件功能的文件度來分類 的)。
1.1程序文件:
這類文件包含源程序文件、目標(biāo)文件或可執(zhí)行程序。
源程序文件:
通常以 .c、.cpp、.java 等作為后綴,它們包含程序員編寫的源代碼,這些源代碼需要被編譯器或解釋器轉(zhuǎn)換為可執(zhí)行代碼才能在計(jì)算機(jī)上運(yùn)行。
目標(biāo)文件:
在編譯過程中產(chǎn)生的中間文件,例如在 Windows 環(huán)境下通常以 .obj 為后綴。這些文件包含編譯器從源程序文件生成的機(jī)器語言代碼,但通常還不能直接運(yùn)行,需要鏈接器進(jìn)一步處理。
可執(zhí)行程序:
這是經(jīng)過編譯和鏈接后生成的文件,可以直接在計(jì)算機(jī)上運(yùn)行。在 Windows 環(huán)境下,可執(zhí)行程序通常以 .exe 為后綴。
1.2數(shù)據(jù)文件:
文件的內(nèi)容不?定是程序,?是程序運(yùn)行時(shí)讀寫的數(shù)據(jù),比如程序運(yùn)行需要從中讀取數(shù)據(jù)的文件,或者輸出內(nèi)容的文件。
本章討論的是數(shù)據(jù)文件。
在以前各章所處理數(shù)據(jù)的輸入輸出都是以終端為對(duì)象的,即從終端的鍵盤輸?數(shù)據(jù),運(yùn)行結(jié)果顯示到顯示器上。
其實(shí)有時(shí)候我們會(huì)把信息輸出到磁盤上,當(dāng)需要的時(shí)候再?gòu)拇疟P上把數(shù)據(jù)讀取到內(nèi)存中使?,這?處 理的就是磁盤上文件。
1.3文件名
?個(gè)文件要有?個(gè)唯?的文件標(biāo)識(shí),以便用戶識(shí)別和引用。
文件名包含3部分:文件路徑+文件名主干+文件后綴
二.文件的打開與關(guān)閉
2.1流和標(biāo)準(zhǔn)流
流
我們程序的數(shù)據(jù)需要輸出到各種外部設(shè)備,也需要從外部設(shè)備獲取數(shù)據(jù),不同的外部設(shè)備的輸?輸出 操作各不相同,為了?便程序員對(duì)各種設(shè)備進(jìn)行方便的操作,我們抽象出了流的概念,我們可以把流想象成流淌著字符的河。
C程序針對(duì)文件、畫面、鍵盤等的數(shù)據(jù)輸?輸出操作都是通過流操作的。?般情況下,我們要想向流里寫數(shù)據(jù),或者從流中讀取數(shù)據(jù),都是要打開流,然后操作。
標(biāo)準(zhǔn)流
那為什么我們從鍵盤輸?數(shù)據(jù),向屏幕上輸出數(shù)據(jù),并沒有打開流呢?
那是因?yàn)镃語?程序在啟動(dòng)的時(shí)候,默認(rèn)打開了3個(gè)流:
?stdin標(biāo)準(zhǔn)輸?流,在大多的環(huán)境中從鍵盤輸?,scanf函數(shù)就是從標(biāo)準(zhǔn)輸?流中讀取數(shù)據(jù)。
?? stdout標(biāo)準(zhǔn)輸出流,大多數(shù)的環(huán)境中輸出至顯示器界?,printf函數(shù)就是將信息輸出到標(biāo)準(zhǔn)輸出 流中。
? stderr標(biāo)準(zhǔn)錯(cuò)誤流,大多數(shù)環(huán)境中輸出到顯示器界面。
這是默認(rèn)打開了這三個(gè)流,我們使用scanf、printf等函數(shù)就可以直接進(jìn)行輸?輸出操作的。
?stdin、stdout、stderr三個(gè)流的類型是: FILE* ,通常稱為文件指針。
2.2 文件指針
緩沖文件系統(tǒng)中,關(guān)鍵的概念是“文件類型指針”,簡(jiǎn)稱“文件指針”。
文件指針在C語言中是一個(gè)用于指向文件的變量。這個(gè)指針通常是一個(gè)指向FILE結(jié)構(gòu)體類型的指針。
VS2022編譯環(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類型構(gòu)的包含的內(nèi)容不完全相同,但是大同小異。每當(dāng)打開?個(gè)文件的時(shí)候,系統(tǒng)會(huì)根據(jù)文件的情況自動(dòng)創(chuàng)建?個(gè)FILE結(jié)變量,并填充其中的信 息,使用者不必關(guān)心細(xì)節(jié)。
?般都是通過?個(gè)FILE的指針來維護(hù)這個(gè)FILE結(jié)構(gòu)的變量,這樣使?起來更加方便。
FILE* pf;
定義pf是?個(gè)指向FILE類型數(shù)據(jù)的指針變量。可以使pf指向某個(gè)文件的文件信息區(qū)(是?個(gè)結(jié)構(gòu)體變 量)。通過該文件信息區(qū)中的信息就能夠訪問該文件。也就是說,通過文件指針變量能夠間接找到與 它關(guān)聯(lián)的文件。
?2.3文件的打開與關(guān)閉
文件在讀寫之前應(yīng)該先打開?件,在使?結(jié)束之后應(yīng)該關(guān)閉文件。
在編寫程序的時(shí)候,在打開文件的同時(shí),都會(huì)返回?個(gè)FILE*的指針變量指向該文件,也相當(dāng)于建立了 指針和文件的關(guān)系。
ANSIC規(guī)定使用?fopen 函數(shù)來打開文件, fclose 來關(guān)閉文件。
fopen
函數(shù)原型:
FILE *fopen(const char *filename, const char *mode);
filename:要打開或創(chuàng)建的文件的名稱(包括路徑,如果需要的話)。 mode:指定文件打開模式的字符串。這些模式字符串決定了文件應(yīng)該如何被打開以及你可以如何使用這個(gè)文件。
文件的打開模式:
文件的使用方式含義如果指定不存在“r”(只讀)為了輸?數(shù)據(jù),打開?個(gè)已經(jīng)存在的文本文件出錯(cuò)“w”(只寫)為了輸出數(shù)據(jù),打開?個(gè)文本文件建立一個(gè)新的文件“a”(追加)向文本文件尾添加數(shù)據(jù)建立一個(gè)新的文件rb”(只讀為了輸入數(shù)據(jù),打開?個(gè)?進(jìn)制文件出錯(cuò)“wb”(只寫)為了輸出數(shù)據(jù),打開?個(gè)?進(jìn)制文件建立一個(gè)新的文件“ab”(追加)向?個(gè)?進(jìn)制文件尾添加數(shù)據(jù)建立?個(gè)新的文件“r+”(讀寫)為了讀和寫,打開?個(gè)文本文件出錯(cuò)“w+”(讀寫為了讀和寫,建議?個(gè)新的文件建立一個(gè)新的文件“a+”(讀寫打開?個(gè)文件,在文件尾進(jìn)行讀寫建立一個(gè)新的文件“rb+”(讀寫)為了讀和寫打開?個(gè)?進(jìn)制文件出錯(cuò)“wb+”(讀 寫)為了讀和寫,新建?個(gè)新的?進(jìn)制文件建立?個(gè)新的文件“ab+”(讀 寫)打開?個(gè)?進(jìn)制文件,在文件尾進(jìn)?讀和寫建立?個(gè)新的文件
fclose
函數(shù)原型:
int fclose(FILE *stream);
FILE *stream:這是一個(gè)指向 FILE 對(duì)象的指針,該對(duì)象標(biāo)識(shí)了要被關(guān)閉的文件。這個(gè)指針通常是通過 fopen、freopen 或 fdopen 函數(shù)返回的。
返回值:如果成功關(guān)閉了文件,fclose 返回零(0)。如果發(fā)生錯(cuò)誤,返回非零值(EOF )。
代碼實(shí)現(xiàn):
#include
int main()
{
FILE* fp;
// 以寫入模式打開文件
fp = fopen("test.txt", "w");
if (fp == NULL)
{
printf("無法打開文件\n");
return 1;
}
// 寫入數(shù)據(jù)到文件
fprintf(fp, "Hello, World!\n");
// 關(guān)閉文件
fclose(fp);
return 0;
}
?三.文件的順序讀寫
3.1順序讀寫函數(shù)介紹
函數(shù)名功能適用于fgetc字符輸?函數(shù)所有輸?流fputc字符輸出函數(shù)所有輸?流fgets文本行輸?函數(shù)所有輸?流fputs文本行輸出函數(shù)所有輸?流fscanf格式化輸?函數(shù)所有輸?流fprintf格式化輸出函數(shù)所有輸?流fread?進(jìn)制輸?文件fwrite?進(jìn)制輸出文件
fgetc
代碼實(shí)現(xiàn):
????????
#include
int main()
{
FILE* pf;
char ch;
// 打開文件
pf = fopen("test.txt", "r");
if (pf == NULL) {
perror(" fopen fail");
return 1;
}
// 逐字符讀取文件內(nèi)容
while ((ch = fgetc(pf)) != EOF)
{
printf("%c",ch); // 輸出讀取到的字符
}
// 關(guān)閉文件
if (fclose(pf) != 0) {
perror("fclos fail");
return 1;
}
return 0;
}
fputc
函數(shù)原型:
int fputc(int character, FILE *stream);
character:需要寫入的字符。雖然該參數(shù)被定義為 int 類型,但實(shí)際上它只使用 int 類型的低八位,并將其作為無符號(hào)字符來處理。
stream:要寫入字符的文件流。這通常是通過 fopen 函數(shù)打開的文件。
#include
int main()
{
FILE* pf;
// 打開文件
pf = fopen("test.txt", "r");
if (pf == NULL)
{
perror("fopen fail");
return 1;
}
// 將字符 'A' 寫入文件
fputc('A', pf);
int ch = fgetc(pf);
printf("讀取的字符 %c",ch);
// 關(guān)閉文件
if (fclose(pf) != 0)
{
perror("fclose fail");
return 1;
}
return 0;
}
?輸出結(jié)果:
fgets
函數(shù)原型:
char *fgets(char *str, int n, FILE *stream);
str:指向一個(gè)字符數(shù)組的指針,該數(shù)組將存儲(chǔ)從文件讀取的字符串。 n:指定要讀取的最大字符數(shù)(包括最后的空字符,但通常不包括換行符)。通常,這個(gè)值設(shè)置為數(shù)組的大小。 stream:指向要讀取的文件的指針。
返回值:
如果成功,fgets 將返回一個(gè)指向 str 的指針。 如果發(fā)生錯(cuò)誤或到達(dá)文件末尾(EOF),fgets 將返回 NULL。
fputs
函數(shù)原型:
int fputs(const char *str, FILE *stream);
?str:要寫入文件的字符串。 stream:一個(gè)指向 FILE 對(duì)象的指針,該對(duì)象標(biāo)識(shí)了要寫入字符的流?
代碼實(shí)現(xiàn): ?
#include
int main()
{
FILE* pf;
char* arr = "hello world";
// 打開目標(biāo)文件
pf = fopen("test.txt", "w"); //寫入數(shù)據(jù)
if (pf == NULL)
{
perror("fopen fail");
return 1;
}
fputs(arr, pf);
//關(guān)閉文件
fclose(pf);
pf = fopen("test.txt", "r");//讀取數(shù)據(jù)
if (pf == NULL)
{
perror("fputs fail");
return 1;
}
char line[100];
while (fgets(line, sizeof(line), pf) != NULL)
{
printf("%s", line);
}
// 關(guān)閉文件
fclose(pf);
return 0;
}
四.文件的隨機(jī)讀寫
4.1 fseek
函數(shù)原型:
int fseek ( FILE * stream, long int offset, int origin );
其中,參數(shù)stream表示文件指針;offset表示要移動(dòng)的字節(jié)數(shù),可以是正數(shù)或負(fù)數(shù);origin表示起始位置
有三種取值:
SEEK_SET從文件開頭開始計(jì)算偏移量SEEK_CUR從當(dāng)前位置開始計(jì)算偏移量SEEK_END從文件末尾開始計(jì)算偏移量
#include
int main()
{
FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{
perror("打開文件失敗");
return 1;
}
// 將文件指針移動(dòng)到文件的第四個(gè)字節(jié)
fseek(pf, 3, SEEK_SET); // SEEK_SET表示從文件開始位置計(jì)算偏移量
// 讀取當(dāng)前文件指針指向的字符
int ch1 = fgetc(pf);
if (ch1 != EOF)
{
printf("第四個(gè)字符是:%c\n", ch1);
}
else
{
printf("讀取第四個(gè)字符失敗\n");
}
if (fseek(pf, -1, SEEK_END) == 0) // -1表示向前移動(dòng)一個(gè)字節(jié)
{
// 讀取當(dāng)前指針指向的字符
int ch2 = fgetc(pf);
if (ch2 != EOF)
{
printf("文件末尾之前的字符是:%c\n", ch2);
}
else
{
printf("讀取失敗\n");
}
}
else
{
printf("無法定位到文件末尾之前的位置\n");
}
//關(guān)閉文件
fclose(pf);
return 0;
}
?輸出結(jié)果:
4.2 ftell
函數(shù)原型:
long ftell(FILE *stream);
stream:這是一個(gè)指向 FILE 對(duì)象的指針,該對(duì)象標(biāo)識(shí)了一個(gè)打開的文件流。
#include
int main() {
FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 1;
}
fseek(pf,0,SEEK_END);
long size = ftell(pf);
// 關(guān)閉文件
fclose(pf);
printf("文件長(zhǎng)度:%ld\n",size);
return 0;
}
4.3 rewind
作用是將文件內(nèi)部的位置指針重新指向一個(gè)流的開頭
函數(shù)原型:
void rewind ( FILE * stream );
stream為已打開文件的指針。
#include
int main()
{
int n;
FILE* pFile;
char buffer[27];
pFile = fopen("myfile.txt", "w+");
for (n = 'A'; n <= 'Z'; n++)
fputc(n, pFile);
rewind(pFile);//回到起始位置
fread(buffer, 1, 26, pFile);//讀取數(shù)據(jù)
fclose(pFile);
buffer[26] = '\0';
printf(buffer);
return 0;
}
五.文件讀取結(jié)束的判定
被錯(cuò)誤使?的 feof 牢記:在文件讀取過程中,不能用feof函數(shù)的返回值直接來判斷文件的是否結(jié)束。?feof 的作用是:當(dāng)?件讀取結(jié)束的時(shí)候,判斷是讀取結(jié)束的原因是否是:遇到文件尾結(jié)束。
1.文本文件讀取是否結(jié)束,判斷返回值是否為 EOF ( fgetc ),或者 NULL ( fgets )
例如:
? fgetc 判斷是否為 EOF 。
? fgets 判斷返回值是否為 NULL 。
文件緩沖區(qū)
ANSIC標(biāo)準(zhǔn)采用“緩沖文件系統(tǒng)”處理的數(shù)據(jù)文件的,所謂緩沖文件系統(tǒng)是指系統(tǒng)自動(dòng)地在內(nèi)存中為 程序中每?個(gè)正在使用的文件開辟?塊“文件緩沖區(qū)”。從內(nèi)存向磁盤輸出數(shù)據(jù)會(huì)先送到內(nèi)存中的緩 沖區(qū),裝滿緩沖區(qū)后才一起送到磁盤上。如果從磁盤向計(jì)算機(jī)讀入數(shù)據(jù),則從磁盤文件中讀取數(shù)據(jù)輸 入到內(nèi)存緩沖區(qū)(充滿緩沖區(qū)),然后再?gòu)木彌_區(qū)逐個(gè)地將數(shù)據(jù)送到程序數(shù)據(jù)區(qū)(程序變量等)。緩沖區(qū)的大小根據(jù)C編譯系統(tǒng)決定的。
#include
#include
//VS2019 WIN11環(huán)境測(cè)試
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ū)時(shí),才將輸出緩沖區(qū)的數(shù)據(jù)寫到?件(磁盤)
//注:fflush 在?版本的VS上不能使?了
printf("再睡眠10秒-此時(shí),再次打開test.txt?件,?件有內(nèi)容了\n");
Sleep(10000);
fclose(pf);
//注:fclose在關(guān)閉?件的時(shí)候,也會(huì)刷新緩沖區(qū)
pf = NULL;
return 0;
}
?刷新前:
刷新后:
這里可以得出一個(gè)結(jié)論: ?
因?yàn)橛芯彌_區(qū)的存在,C語?在操作文件的時(shí)候,需要做刷新緩沖區(qū)或者在?件操作結(jié)束的時(shí)候關(guān)閉? 件。 如果不做,可能導(dǎo)致讀寫文件的問題。
柚子快報(bào)激活碼778899分享:開發(fā)語言 C語言之旅:文件操作
相關(guān)閱讀
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場(chǎng)。
轉(zhuǎn)載請(qǐng)注明,如有侵權(quán),聯(lián)系刪除。