柚子快報邀請碼778899分享:開發(fā)語言 C語言:文件操作
柚子快報邀請碼778899分享:開發(fā)語言 C語言:文件操作
目錄
?
為什么使用文件
什么是文件?
程序文件
數(shù)據(jù)?件
文件名
二進制文件和文本文件
文件的打開和關(guān)閉
流和標準流、
流
標準流
文件指針
文件的打開和關(guān)閉
文件打開方式
文件的順序讀寫
fputc函數(shù)示范(寫字符)
fgetc函數(shù)示范(讀字符)
(字符)stdin標準輸入流和stdout標準輸出流
fputs函數(shù)的示范(寫字符串)
fgets函數(shù)示范(讀字符串)
(字符串)stdin標準輸入流和stdout標準輸出流
fprintf(寫整數(shù),浮點數(shù),字符,字符串)
fscanf(讀整數(shù),浮點數(shù),字符,字符串)
(整數(shù),浮點數(shù),字符,字符串)stdin標準輸入流和stdout標準輸出流
對比一組函數(shù):
fwrite函數(shù)
fread函數(shù)
文件的隨機讀寫
fseek
從文件開頭向后偏移
從光標位置偏移
文件末尾向前面偏移
ftell函數(shù)
?rewind函數(shù)
?件讀取結(jié)束的判定
拷貝字符串代碼
文件緩沖區(qū)
為什么使用文件
如果沒有?件,我們寫的程序的數(shù)據(jù)是存儲在電腦的內(nèi)存中,如果程序退出,內(nèi)存回收,數(shù)據(jù)就丟失了,等再次運?程序,是看不到上次程序的數(shù)據(jù)的,如果要將數(shù)據(jù)進?持久化的保存,我們可以使??件,因為文件是存放在硬盤上的
什么是文件?
磁盤上的?件和文件夾都是?件。 但是在程序設(shè)計中,我們?般談的?件有兩種:程序?件、數(shù)據(jù)?件(從?件功能的?度來分類 的)。
程序文件
程序?件包括源程序?件(后綴為.c),
?標?件(windows環(huán)境后綴為.obj),
可執(zhí)?程序(windows環(huán)境后綴為.exe)。
程序文件
目標文件
可執(zhí)行程序
數(shù)據(jù)?件
?件的內(nèi)容不?定是程序,?是程序運?時讀寫的數(shù)據(jù),?如程序運?需要從中讀取數(shù)據(jù)的?件,或者輸出內(nèi)容的?件。
本篇討論的是數(shù)據(jù)?件。
在以前各章所處理數(shù)據(jù)的輸?輸出都是以終端為對象的,即從終端的鍵盤輸?數(shù)據(jù),運?結(jié)果顯?到顯?器上。
其實有時候我們會把信息輸出到磁盤上,當(dāng)需要的時候再從磁盤上把數(shù)據(jù)讀取到內(nèi)存中使?,這?處理的就是磁盤上?件。
文件名
?個?件要有?個唯?的?件標識,以便??識別和引?。 ?件名包含3部分:?件路徑+?件主?名+?件后綴 例如: c:\code\test.txt 為了?便起?,?件標識常被稱為?件名。
二進制文件和文本文件
根據(jù)數(shù)據(jù)的組織形式,數(shù)據(jù)?件被稱為?本?件或者?進制?件。 數(shù)據(jù)在內(nèi)存中以?進制的形式存儲,如果不加轉(zhuǎn)換的輸出到外存,就是?進制?件。 如果要求在外存上以ASCII碼的形式存儲,則需要在存儲前轉(zhuǎn)換。以ASCII字符的形式存儲的?件就是?本?件。
?個數(shù)據(jù)在內(nèi)存中是怎么存儲的呢 字符?律以ASCII形式存儲,數(shù)值型數(shù)據(jù)既可以?ASCII形式存儲,也可以使??進制形式存儲。 如有整數(shù)10000,如果以ASCII碼的形式輸出到磁盤,則磁盤中占?5個字節(jié)(每個字符?個字節(jié)),??進制形式輸出,則在磁盤上只占4個字節(jié)(VS2019測試)。
?進制形式輸出不一定占用字節(jié)小,比如給一個整數(shù)1,ASCII碼形式輸出占?1個字節(jié),二進制的占?4個字節(jié)
文本文件我們是可以看到的
二進制
下面這個fwite(a的地址,多少字節(jié),寫多少次,寫到關(guān)聯(lián)的p)
int main()
{
int a = 10000;
FILE* p = fopen("test.txt", "wb");//打開文件
fwrite(&a, 4, 1, p);//寫入二進制
fclose(p);//關(guān)閉文件
p = NULL;
return 0;
}
輸出的東西我們都看不懂,因為存放的是二進制
我們可以用vs的二進制編輯器打開查看
這個是小端存放的,所以是倒過來的
文件的打開和關(guān)閉
流和標準流、
流
我們程序的數(shù)據(jù)需要輸出到各種外部設(shè)備,也需要從外部設(shè)備獲取數(shù)據(jù),不同的外部設(shè)備的輸?輸出操作各不相同,為了?便程序員對各種設(shè)備進??便的操作,我們抽象出了流的概念,我們可以把流想象成流淌著字符的河。C程序針對?件、畫?、鍵盤等的數(shù)據(jù)輸?輸出操作都是通過流操作的。 ?般情況下,我們要想向流?寫數(shù)據(jù),或者從流中讀取數(shù)據(jù),都是要打開流,然后操作。
標準流
為什么我們從鍵盤上輸入數(shù)據(jù),向屏幕輸出數(shù)據(jù),并沒有打開流呢?
那是因為C語?程序在啟動的時候,默認打開了3個流:
stdin --?標準輸?流,在?多數(shù)的環(huán)境中從鍵盤輸?,scanf函數(shù)就是從標準輸?流中讀取數(shù)據(jù)。stdout -- 標準輸出流,?多數(shù)的環(huán)境中輸出?顯?器界?,printf函數(shù)就是將信息輸出到標準輸出流中。stderr -- 標準錯誤流,?多數(shù)環(huán)境中輸出到顯?器界?。
這是默認打開了這三個流,我們使?scanf、printf等函數(shù)就可以直接進?輸?輸出操作的。stdin、stdout、stderr三個流的類型是: FILE* ,通常稱為?件指針。 C語?中,就是通過 FILE* 的?件指針來維護流的各種操作的。
文件指針
緩沖?件系統(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)?細節(jié)。 ?般都是通過?個FILE的指針來維護這個FILE結(jié)構(gòu)的變量,這樣使?起來更加?便。
下?我們可以創(chuàng)建?個FILE*的指針變量:
FILE* p;//文件指針變量
定義pf是?個指向FILE類型數(shù)據(jù)的指針變量??梢允筽f指向某個?件的?件信息區(qū)(是?個結(jié)構(gòu)體變量)。通過該?件信息區(qū)中的信息就能夠訪問該?件。也就是說,通過?件指針變量能夠間接找到與它關(guān)聯(lián)的?件。
比如:
文件的打開和關(guān)閉
打開文件就是打開流
讀寫文件就是讀寫流
關(guān)閉文件就是關(guān)閉流
我們只需要關(guān)注文件操作進行,流的操作是底層的操作我們不用關(guān)心
?件在讀寫之前應(yīng)該先打開?件,在使?結(jié)束之后應(yīng)該關(guān)閉?件。
在編寫程序的時候,在打開?件的同時,都會返回?個FILE*的指針變量指向該?件,也相當(dāng)于建?了指針和?件的關(guān)系。
ANSIC?規(guī)定使? fopen 函數(shù)來打開?件, fclose 來關(guān)閉?件。
int main()
{
//打開文件
//打開文件成功,返回有效指針
//打開失敗,返回NULL
//當(dāng)然也可以用路徑打開文件
FILE* p = fopen("data.txt", "w");
//判斷是不是NULL
if (p == NULL)
{
//是就報錯
perror("fopen");
return 1;
}
//關(guān)閉文件
fclose(p);
//給p賦值NULL
p = NULL;
return 0;
}
文件打開方式
?件使??式含義如果指定?件不存在“r”(只讀)為了輸?數(shù)據(jù),打開?個已經(jīng)存在的?本?件出錯“w”(只寫)為了輸出數(shù)據(jù),打開?個?本?件建??個新的?件“a”(追加)向?本?件尾添加數(shù)據(jù)建??個新的?件“rb”(只讀)為了輸?數(shù)據(jù),打開?個?進制?件出錯“wb”(只寫)為了輸出數(shù)據(jù),打開?個?進制?件?建??個新的?件“ab”(追加)向?個?進制?件尾添加數(shù)據(jù)?建??個新的?件“r+”(讀寫)為了讀和寫,打開?個?本?件出錯“w+”(讀寫)為了讀和寫,建議?個新的?件建??個新的?件“a+”(讀寫)打開?個?件,在?件尾進?讀寫建??個新的?件“rb+”(讀寫)為了讀和寫打開?個?進制?件出錯“wb+”(讀寫)為了讀和寫,新建?個新的?進制?件建??個新的?件“ab+”(讀寫)打開?個?進制?件,在?件尾進?讀和寫建??個新的?件
文件的順序讀寫
順序讀寫函數(shù)介紹:
前6個函數(shù)是讀和寫的都是文本信息,后面2個是二進制信息
函數(shù)名功能適用于fgetc字符輸?函數(shù)(讀字符)所有輸?流fputc字符輸出函數(shù)(寫字符)所有輸出流fgets?本?輸?函數(shù)(讀字符串)所有輸?流fputs?本?輸出函數(shù)(寫字符串)所有輸出流fscanf格式化輸?函數(shù)(可以讀所有類型)所有輸?流fprintf格式化輸出函數(shù)所有輸出流fread二進制輸入文件fwrite二進制輸出文件
l
fputc函數(shù)示范(寫字符)
fputc是寫字符的,這個函數(shù)一次只能輸出一個字符
int main()
{
//打開文件
FILE* p = fopen("data.txt", "w");
if (p == NULL)
{
perror("fopen");
return 1;
}
//寫入文件
fputc('a', p);
fputc('b', p);
fputc('c', p);
fputc('d', p);
fputc('e', p);
fputc('f', p);
//關(guān)閉文件
fclose(p);
p = NULL;
return 0;
}
fputc可以循環(huán)寫入
int main()
{
//打開文件
FILE* p = fopen("data.txt", "w");
if (p == NULL)
{
perror("fopen");
return 1;
}
//寫入文件
for (int i = 'a'; i <= 'z'; i++)
{
fputc(i, p);
}
//關(guān)閉文件
fclose(p);
p = NULL;
return 0;
}
fgetc函數(shù)示范(讀字符)
讀取正常的時候,返回讀取到的字符的ASCII碼值
讀取失敗的時候,返回EOF
EOF是一個文件的結(jié)束標志
下面這個代碼我們可以看到從文件里讀取了5個字符。
int main()
{
//打開文件
FILE* p = fopen("data.txt", "r");
if (p == NULL)
{
perror("fopen");
return 1;
}
//讀文件
int r = fgetc(p);
printf("%c\n", r);
r = fgetc(p);
printf("%c\n", r);
r = fgetc(p);
printf("%c\n", r);
r = fgetc(p);
printf("%c\n", r);
r = fgetc(p);
printf("%c\n", r);
//關(guān)閉文件
fclose(p);
p = NULL;
}
我們也可以用循環(huán)的方式進行讀
int main()
{
//打開文件
FILE* p = fopen("data.txt", "r");
if (p == NULL)
{
perror("fopen");
return 1;
}
//讀文件
int r = 0;
while ((r = fgetc(p)) != EOF)
{
printf("%c\n", r);
}
//關(guān)閉文件
fclose(p);
p = NULL;
}
(字符)stdin標準輸入流和stdout標準輸出流
stdin --?標準輸?流,在?多數(shù)的環(huán)境中從鍵盤輸?,scanf函數(shù)就是從標準輸?流中讀取數(shù)據(jù)。stdout -- 標準輸出流,?多數(shù)的環(huán)境中輸出?顯?器界?,printf函數(shù)就是將信息輸出到標準輸出流中。
int main()
{
int r = fgetc(stdin);//標準輸入流(從鍵盤讀)
fputc(r, stdout);//標準輸出流(寫到屏幕上)
return 0;
}
fputs函數(shù)的示范(寫字符串)
這個函數(shù)可以往文件里寫字符串
int main()
{
//打開文件
FILE* p = fopen("data.txt", "w");
if (p == NULL)
{
perror("fopen");
return 1;
}
//寫入文件
fputs("您好小明,今天有空嗎", p );
//關(guān)閉文件
fclose(p);
p = NULL;
}
fgets函數(shù)示范(讀字符串)
這個函數(shù)是從文件里,讀取字符串出來
下面這代碼,我們要讀取3個字符,我們可以看到實際上只讀取了2個字符,編譯器會在后面加個\0
下面這個代碼,沒有讀取到w,這是為什么呢,因為這個函數(shù)是文本行的讀取,所以讀取到\n會停下來,然后呢編譯器在后面加\0
(字符串)stdin標準輸入流和stdout標準輸出流
int main()
{
char arr[20] = { 0 };
//從鍵盤讀數(shù)據(jù)
fgets(arr, 10, stdin);
//寫到屏幕上
fputs(arr, stdout);
}
fprintf(寫整數(shù),浮點數(shù),字符,字符串)
fprintf和printf的寫法是一樣的只不過fprintf是將數(shù)值寫到文件里,所以第一個參數(shù)是流。
只要是printf能打印的,fprintf都能寫到文件里
int main()
{
char a[20] = "小明";
int b = 18;
float c = 75.5f;
//打開文件
FILE* p = fopen("data.txt", "w");
if (p == NULL)
{
perror("fopen");
return 1;
}
//寫入文件
fprintf(p ,"%s %d %f", a, b, c);
//關(guān)閉文件
fclose(p);
p = NULL;
}
下面這代碼我們也可以用結(jié)構(gòu)體的方式
fscanf(讀整數(shù),浮點數(shù),字符,字符串)
fscanf和scanf也是差不多一樣的,scanf是讀取鍵盤,fscanf是讀取文件數(shù)據(jù)
下面這個代碼是讀取文件的數(shù)據(jù)放到結(jié)構(gòu)體變量里,然后進行打印結(jié)構(gòu)體
struct a
{
char a[20];
int b;
float c;
};
int main()
{
struct a arr = { 0 };
//打開文件
FILE* p = fopen("data.txt", "r");
if (p == NULL)
{
perror("fopen");
return 1;
}
//讀文件
fscanf(p , "%s %d %f", &arr.a, &arr.b, &arr.c);
//打印到屏幕上
printf("%s %d %.2f", arr.a, arr.b, arr.c);
//關(guān)閉文件
fclose(p);
p = NULL;
}
(整數(shù),浮點數(shù),字符,字符串)stdin標準輸入流和stdout標準輸出流
struct a
{
char a[20];
int b;
float c;
};
int main()
{
struct a arr = { 0 };
//讀取鍵盤
fscanf(stdin, "%s %d %f", &arr.a, &arr.b, &arr.c);
//打印到屏幕上
fprintf(stdout,"%s %d %.2f", arr.a, arr.b, arr.c);
}
對比一組函數(shù):
scanf / fscanf /?sscanf printf / fprintf / sprintf
scanf和printf? ?針對標準輸入流/標準輸出流的 格式化 輸入/輸出函數(shù)
fscanf和fprintf??針對所有輸入流/所有輸出流的 格式化 輸入/輸出函數(shù)
sprintf:將格式化的數(shù)據(jù)轉(zhuǎn)換成字符串。
sscanf:從字符串中提取格式化的數(shù)據(jù)。
sprintf:其實是將格式化的數(shù)據(jù)寫到字符串中(可以理解為將格式化的數(shù)據(jù)轉(zhuǎn)換成字符串)
如果有很多數(shù)據(jù)需要整合成字符串就可以使用sprintf
struct a
{
char a[20];
int b;
float c;
};
int main()
{
char arr[99] = { 0 };
struct a p = { "小虎",33,65.9};
//將p里的數(shù)據(jù)轉(zhuǎn)換成字符串,放到arr數(shù)組中
sprintf(arr,"%s %d %.2f", p.a, p.b, p.c);
printf("%s", arr);
return 0;
}
sscanf:是從字符串中提取格式化的數(shù)據(jù),可以理解為將字符串轉(zhuǎn)換成格式化的數(shù)據(jù)
struct a
{
char a[20];
int b;
float c;
};
int main()
{
char arr[99] = { 0 };
struct a p = { "小虎",33,65.9 };
//將p里的數(shù)據(jù)轉(zhuǎn)換成字符串,放到arr數(shù)組中
sprintf(arr, "%s %d %.2f", p.a, p.b, p.c);
struct a kk = { 0 };
//從字符串里讀取格式化的數(shù)據(jù),轉(zhuǎn)換后,放到kk中
sscanf(arr,"%s %d %f", kk.a, &kk.b, &kk.c);
//打印
printf("%s %d %.2f", kk.a, kk.b, kk.c);
return 0;
}
fwrite函數(shù)
這個函數(shù)是以二進制形式寫到文件里,
fwrite第一個參數(shù)是數(shù)據(jù),第二個參數(shù)是類型的大小,第三個參數(shù)是要寫多少,第四個參數(shù)是流(把數(shù)據(jù)寫到文件里)。
下面這代碼我們可以看到以二進制文件寫到文件里,二進制我們當(dāng)然是看不懂的。
struct a
{
char a[20];
int b;
float c;
};
int main()
{
struct a ps = { "asdfyg",25,88.8f };
//打開文件
FILE* p = fopen("data.txt", "wb");
if (p == NULL)
{
perror("fopen");
return 1;
}
//寫入二進制到文件
fwrite(&ps, sizeof(struct a), 1, p);
//關(guān)閉文件
fclose(p);
p = NULL;
return 0;
}
fread函數(shù)
這個函數(shù)是讀取二進制信息
fwrite第一個參數(shù)是讀到的數(shù)據(jù)放的位置,第二個參數(shù)是類型的大小,第三個參數(shù)是要讀多少,第四個參數(shù)是流(從流讀取數(shù)據(jù))。
下面這代碼我們可以看到,從二進制讀取信息,打印在屏幕上
struct a
{
char a[20];
int b;
float c;
};
int main()
{
struct a ps = { 0 };
//打開文件
FILE* p = fopen("data.txt", "rb");
if (p == NULL)
{
perror("fopen");
return 1;
}
//讀取二進制文件
fread(&ps,sizeof(struct a), 1 , p );
//打印
printf("%s %d %f", ps.a, ps.b, ps.c);
//關(guān)閉文件
fclose(p);
p = NULL;
}
文件的隨機讀寫
順序讀寫是一行一行的讀寫,隨機讀寫是想讀寫那個就讀寫那個
fseek
fseek根據(jù)?件指針的位置和偏移量來定位?件指針(文件內(nèi)容的光標)。
第一個參數(shù)是流,第二個參數(shù)是偏移量,
第三個參數(shù)定位文件指針:
SEEK_SET(是文件開頭的位置)
SEEK_CUR(文件指針的當(dāng)前位置(光標))
SEEK_END(文件末尾)
作用是:
用SEEK_SET從文件開頭的位置偏移到后面
用SEEK_CUR從光標的位置向后面偏移
用SEEK_END文件末尾向前面偏移
向前面偏移用正數(shù),向后偏移用負數(shù)
從文件開頭向后偏移
讀取a和b后,定位文件指針,SEEK_SET從文件開頭向后偏移了6,讀取了后面的3個hhh
從光標位置偏移
當(dāng)我們讀取了a和b光標會在b的后面,定位文件指針,SEEK_CUR從光標的位置向后面偏移4個,讀取了后面的3個hhh。
文件末尾向前面偏移
SEEK_END從文件末尾向前面偏移了-6,讀取了后面的3個d e f
ftell函數(shù)
ftell返回?件指針相對于起始位置的偏移量。
ftell計算從起始位置到光標的偏移量。
int main()
{
//打開文件
FILE* p = fopen("data.txt", "r");
if (p == NULL)
{
perror("fopen");
return 1;
}
//讀文件
int r = 0;
//定位文件指針
fseek(p, -6, SEEK_END);
r = fgetc(p);//d
printf("%c\n", r);
r = fgetc(p);//e
printf("%c\n", r);
r = fgetc(p);//f
printf("%c\n", r);
printf("%d\n", ftell(p));
//關(guān)閉文件
fclose(p);
p = NULL;
}
?rewind函數(shù)
讓?件指針的位置回到?件的起始位置
就是讓光標來到起始位置
int main()
{
//打開文件
FILE* p = fopen("data.txt", "r");
if (p == NULL)
{
perror("fopen");
return 1;
}
//讀文件
int r = 0;
r = fgetc(p);//a
printf("%c\n", r);
r = fgetc(p);//b
printf("%c\n", r);
r = fgetc(p);//c
printf("%c\n", r);
//將文件指針重新定位到文件的起始位置
rewind(p);
r = fgetc(p);//a
printf("%c\n", r);
r = fgetc(p);//b
printf("%c\n", r);
r = fgetc(p);//c
printf("%c\n", r);
//關(guān)閉文件
fclose(p);
p = NULL;
}
?件讀取結(jié)束的判定
被錯誤使?的 feof?
牢記:在?件讀取過程中,不能?feof函數(shù)的返回值直接來判斷?件的是否結(jié)束。 feof 的作?是:當(dāng)?件讀取結(jié)束的時候,判斷是讀取結(jié)束的原因是否是:遇到?件尾結(jié)束。
?本?件讀取是否結(jié)束,判斷返回值是否為 EOF ( fgetc ),或者 NULL ( fgets ) 例如:
fgetc 判斷是否為 EOF . fgets 判斷返回值是否為 NULL .
?進制?件的讀取結(jié)束判斷,判斷返回值是否?于實際要讀的個數(shù)。 例如:
?fread判斷返回值是否?于實際要讀的個數(shù)。
EOF也可以拿來文件的結(jié)束標志
feof函數(shù)不是用來判斷文件是否結(jié)束的?。?!
1
在讀取文件的過程中,有可能讀取文件結(jié)束
結(jié)束的原因:
1.遇到文件末尾
2.遇到錯誤了
?本?件的例?:
feof是在已經(jīng)結(jié)束了,判斷結(jié)束的原因是什么。
#include
#include
int main(void)
{
int c; // 注意:int,?char,要求處理EOF
FILE* fp = fopen("test.txt", "r");
if (!fp) {
perror("File opening failed");
return EXIT_FAILURE;
}
//fgetc 當(dāng)讀取失敗的時候或者遇到?件結(jié)束的時候,都會返回EOF
while ((c = fgetc(fp)) != EOF) // 標準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);
}
?進制?件的例?:
#include
enum { SIZE = 5 };
int main(void)
{
double a[SIZE] = { 1.,2.,3.,4.,5. };
FILE* fp = fopen("test.bin", "wb");
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);
}
下面這代碼我們可以看到只讀取了一個字符,沒有遇到文件末尾,feof返回0
當(dāng)我們讀取完到遇到文件末尾返回1
拷貝字符串代碼
#include
#include
int main()
{
//打開文件
FILE* p1 = fopen("data1.txt", "r");
if (p1 == NULL)
{
perror("fopen");
return 1;
}
FILE* p2 = fopen("data2.txt", "w");
if (p2 == NULL)
{
perror("fopen");
fclose(p1);
p1 = NULL;
return 1;
}
//復(fù)制
int r = 0;
while ((r = fgetc(p1)) != EOF)
{
fputc(r, p2);
}
fclose(p1);
p1 == NULL;
fclose(p2);
p2 = NULL;
return 0;
}
文件緩沖區(qū)
ANSIC 標準采?“緩沖?件系統(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)決定的。
系統(tǒng)會在內(nèi)存中開辟一塊文件緩沖區(qū),當(dāng)我們把數(shù)據(jù)寫到文件里(也就是硬盤),會先把數(shù)據(jù)放到輸出緩沖區(qū),放滿了輸出緩沖區(qū),才?起送到硬盤上。
輸入緩沖區(qū)元素一樣,讀的數(shù)據(jù),放滿了輸入緩沖區(qū),然后再從緩沖區(qū)逐個地將數(shù)據(jù)送到程序數(shù)據(jù)區(qū)(程序變量等)。
下面這有個代碼
#include
#include
//VS2019 WIN11環(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;
}
下面這代碼我們可以看到,把字符串放到了輸出緩沖區(qū),但是還沒有刷新緩沖區(qū)(也就是還沒有放到硬盤)
下面代碼,已經(jīng)刷新緩沖區(qū)了(也就是放到硬盤了),我們可以看到,字符串已經(jīng)放到文件(硬盤)里了
這?可以得出?個結(jié)論:因為有緩沖區(qū)的存在,C語?在操作?件的時候,需要做刷新緩沖區(qū)或者在?件操作結(jié)束的時候關(guān)閉?件。如果不做,可能導(dǎo)致讀寫?件的問題。
柚子快報邀請碼778899分享:開發(fā)語言 C語言:文件操作
推薦鏈接
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點和立場。
轉(zhuǎn)載請注明,如有侵權(quán),聯(lián)系刪除。