一文帶你掌握C語(yǔ)言中的文件操作
1. 什么是文件
文件其實(shí)是指一組相關(guān)數(shù)據(jù)的有序集合。這個(gè)數(shù)據(jù)集有一個(gè)名稱,叫做文件名。文件通常是駐留在外部介質(zhì)(如磁盤等)上的,在使用時(shí)才調(diào)入內(nèi)存中來(lái)。
從文件功能上來(lái)講,一般可分為:程序文件與數(shù)據(jù)文件
1.1 文件名
一個(gè)文件要有一個(gè)唯一的文件標(biāo)識(shí),以便用戶識(shí)別和引用,這就是文件名
- ?件名包含3部分:?件路徑+?件名主?+?件后綴
- 例如:
c:\code\test.txt
1.2 程序文件
程序文件一般指:源程序文件(后綴為.c)
,目標(biāo)文件(windows環(huán)境后綴為.obj)
,可執(zhí)行程序(windows環(huán)境后綴為.exe)
源程序文件一般在創(chuàng)建程序目錄文件下
源程序文件經(jīng)過(guò)編譯器鏈接與鏈接器鏈接可以生成我們的可執(zhí)行程序的文件。
1.3 數(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)存中使?,這?處理的就是磁盤上?件。
2. 文件的作用
如果沒(méi)有?件,我們寫的程序的數(shù)據(jù)是存儲(chǔ)在電腦的內(nèi)存中,如果程序退出,內(nèi)存回收,數(shù)據(jù)就丟失了,等再次運(yùn)?程序,是看不到上次程序的數(shù)據(jù)的,如果要將數(shù)據(jù)進(jìn)?持久化的保存,我們就需要使?文件。
3. 文件的打開(kāi)與關(guān)閉
3.1 流與標(biāo)準(zhǔn)流
(1) 流
我們程序的數(shù)據(jù)需要輸出到各種外部設(shè)備,也需要從外部設(shè)備獲取數(shù)據(jù),不同的外部設(shè)備的輸?輸出操作各不相同,為了?便程序員對(duì)各種設(shè)備進(jìn)??便的操作,我們抽象出了流的概念,我們可以把流想象成流淌著字符的河。
C程序針對(duì)文件
、畫面
、 鍵盤
等的數(shù)據(jù)輸?輸出操作都是通過(guò)流操作的。?般情況下,我們要想向流?寫數(shù)據(jù),或者從流中讀取數(shù)據(jù),都是要打開(kāi)流,然后操作。
(2) 標(biāo)準(zhǔn)流
那為什么我們從鍵盤輸?數(shù)據(jù),向屏幕上輸出數(shù)據(jù),并沒(méi)有打開(kāi)流呢?那是因?yàn)镃語(yǔ)?程序在啟動(dòng)的時(shí)候,默認(rèn)打開(kāi)了3個(gè)流:
• stdin-標(biāo)準(zhǔn)輸?流,在?多數(shù)的環(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)打開(kāi)了這三個(gè)流,我們使?scanf、printf等函數(shù)就可以直接進(jìn)?輸?輸出操作的。stdin、stdout、stderr三個(gè)流的類型是: FILE*
,通常稱為文件指針。
在C語(yǔ)?中,就是通過(guò) FILE*
的?件指針來(lái)維護(hù)流的各種操作的。
3.2 文件指針
在緩沖文件系統(tǒng)中,關(guān)鍵的概念是“文件類型指針”,簡(jiǎn)稱“文件指針”。
每個(gè)被使用的文件都在內(nèi)存中開(kāi)辟了一個(gè)相應(yīng)的文件信息區(qū),用來(lái)存放文件的相關(guān)信息(如文件的名字,文件狀態(tài)及文件當(dāng)前的位置等)。這些信息是保存在一個(gè)結(jié)構(gòu)體變量中的。該結(jié)構(gòu)體類型是有系統(tǒng)聲明的,取名【FILE】
例如,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; FILE* pf;//文件指針變量
不同的C編譯器的FILE類型包含的內(nèi)容不完全相同,但是大同小異。每當(dāng)打開(kāi)一個(gè)文件的時(shí)候,系統(tǒng)會(huì)根據(jù)文件的情況自動(dòng)創(chuàng)建一個(gè)FILE結(jié)構(gòu)的變量,并填充其中的信息,使用者不必關(guān)心細(xì)節(jié)。
一般都是通過(guò)一個(gè)FILE的指針來(lái)維護(hù)這個(gè)FILE結(jié)構(gòu)的變量,這樣使用起來(lái)更加方便。我們來(lái)看看如何創(chuàng)建一個(gè)FILE的指針變量
FILE* pf; //文件指針變量
定義pf是一個(gè)指向FILE類型數(shù)據(jù)的指針變量??梢允筽f指向某個(gè)文件的文件信息區(qū)(是一個(gè)結(jié)構(gòu)體變量)。通過(guò)該文件信息區(qū)中的信息就能夠訪問(wèn)該文件。也就是說(shuō),通過(guò)文件指針變量能夠找到與它關(guān)聯(lián)的文件,以此來(lái)進(jìn)行相關(guān)操作。
3.3 文件的打開(kāi)與關(guān)閉
?件在讀寫之前應(yīng)該先打開(kāi)?件,在使?結(jié)束之后應(yīng)該關(guān)閉?件。這與我們前面學(xué)習(xí)的動(dòng)態(tài)內(nèi)存開(kāi)辟很類似。
在編寫程序的時(shí)候,在打開(kāi)?件的同時(shí),都會(huì)返回?個(gè)FILE*
的指針變量指向該?件,也相當(dāng)于建?了指針和?件的關(guān)系。
ANSIC規(guī)定使? fopen
函數(shù)來(lái)打開(kāi)?件, fclose
來(lái)關(guān)閉?件。
(1) fopen函數(shù)
頭文件#include<stdio.h>
聲明:FILE *fopen(const char *filename, const char *mode)
- filename -- 字符串,表示要打開(kāi)的文件名稱。
- mode -- 字符串,表示文件的訪問(wèn)模式。
作用:使用給定的模式 mode 打開(kāi) filename 所指向的文件
返回值:該函數(shù)返回一個(gè) FILE 指針。否則返回 NULL,且設(shè)置全局變量 errno 來(lái)標(biāo)識(shí)錯(cuò)誤。
下表為常見(jiàn)的訪問(wèn)模式(mode):
文件使用方式 | 含義 | 如果指定文件不存在 |
---|---|---|
“r”(只讀) | 為了輸入數(shù)據(jù),打開(kāi)一個(gè)已經(jīng)存在的文本文件 | 出錯(cuò) |
“w”(只寫) | 為了輸出數(shù)據(jù),打開(kāi)一個(gè)文本文件 | 建立一個(gè)新的文件 |
“a”(追加) | 向文本文件尾添加數(shù)據(jù) | 建立一個(gè)新的文件 |
rb”(只讀) | 為了輸入數(shù)據(jù),打開(kāi)一個(gè)二進(jìn)制文件 | 出錯(cuò) |
“wb”(只寫) | 為了輸出數(shù)據(jù),打開(kāi)一個(gè)二進(jìn)制文件 | 建立一個(gè)新的文件 |
“ab”(追加) | 向一個(gè)二進(jìn)制文件尾添加數(shù)據(jù) | 出錯(cuò) |
“r+”(讀寫) | 為了讀和寫,打開(kāi)一個(gè)文本文件 | 出錯(cuò) |
“w+”(讀寫) | 為了讀和寫,建議一個(gè)新的文件 | 建立一個(gè)新的文件 |
“a+”(讀寫) | 打開(kāi)一個(gè)文件,在文件尾進(jìn)行讀寫 | 建立一個(gè)新的文件 |
“rb+”(讀寫) | 為了讀和寫打開(kāi)一個(gè)二進(jìn)制文件 | 出錯(cuò) |
“wb+”(讀寫) | 為了讀和寫,新建一個(gè)新的二進(jìn)制文件 | 建立一個(gè)新的文件 |
“a+”(讀寫) | 打開(kāi)一個(gè)二進(jìn)制文件,在文件尾進(jìn)行讀寫 | 建立一個(gè)新的文件 |
(2) fclose函數(shù)
頭文件#include<stdio.h>
聲明:int fclose(FILE *stream)
stream -- 這是指向 FILE 對(duì)象的指針,該 FILE 對(duì)象指定了要被關(guān)閉的流。
作用:關(guān)閉流 stream。刷新所有的緩沖區(qū)
返回值:如果流成功關(guān)閉,則該方法返回零。如果失敗,則返回 EOF。
下列是fopen與fclose具體使用:
int main() { //打開(kāi)文件 FILE* pf = fopen("test.txt", "w"); if (pf == NULL) { perror(" fopen fail"); return 1; } //關(guān)閉文件 fclose(pf); pf = NULL; //防止野指針 return 0; }
4. 文件的順序讀寫
4.1 單字符輸入輸出
(1) fputc函數(shù)
頭文件:#include<stdio.h>
聲明:int fputc(int char, FILE *stream)
- char -- 這是要被寫入的字符。該字符以其對(duì)應(yīng)的 int 值進(jìn)行傳遞。
- stream -- 這是指向 FILE 對(duì)象的指針,該 FILE 對(duì)象標(biāo)識(shí)了要被寫入字符的流。
作用:把參數(shù) char 指定的字符(一個(gè)無(wú)符號(hào)字符)寫入到指定的流 stream 中。
返回值:如果沒(méi)有發(fā)生錯(cuò)誤,則返回被寫入的字符。如果發(fā)生錯(cuò)誤,則返回 EOF,并設(shè)置錯(cuò)誤標(biāo)識(shí)符。
下列是具體的fputc的使用方法:
#include<stdio.h> int main() { //打開(kāi)文件 FILE* pf = fopen("test.txt", "w"); if (pf == NULL) { perror(" fopen fail"); return 1; } //將abc放進(jìn)文件 fputc('a', pf); fputc('b', pf); fputc('c', pf); //關(guān)閉文件 fclose(pf); pf = NULL;//防止野指針 return 0; }
如果你想查看寫入結(jié)果,可以在創(chuàng)建項(xiàng)目下找到Debug文件,打開(kāi)
(2) fgetc函數(shù)
頭文件:#include<stdio.h>
聲明:int fgetc(FILE *stream)
- stream -- 這是指向 FILE 對(duì)象的指針,該 FILE 對(duì)象標(biāo)識(shí)了要在上面執(zhí)行操作的流。
作用:從指定的流 stream 獲取下一個(gè)字符(一個(gè)無(wú)符號(hào)字符)。
返回值:該函數(shù)以無(wú)符號(hào) char 強(qiáng)制轉(zhuǎn)換為 int 的形式返回讀取的字符,如果到達(dá)文件末尾或發(fā)生讀錯(cuò)誤,則返回 EOF。
下列是具體的fputc的使用方法:
#include<stdio.h> int main() { //打開(kāi)文件 FILE* pf = fopen("test.txt", "r");//只讀 if (pf == NULL) { perror(" fopen fail"); return 1; } fputc('a', pf); fputc('b', pf); fputc('c', pf); int ch = fgetc(pf); printf("讀出來(lái)的字符為:%c\n", ch); ch = fgetc(pf); printf("讀出來(lái)的字符為:%c\n", ch); ch = fgetc(pf); printf("讀出來(lái)的字符為:%c\n", ch); //關(guān)閉文件 fclose(pf); pf = NULL;//防止野指針 return 0; }
輸出結(jié)果:
4.2 文本行輸入輸出
(1) fputs函數(shù)
頭文件:#include<stdio.h>
聲明:int fputs(const char *str, FILE *stream)
- str -- 這是一個(gè)數(shù)組,包含了要寫入的以空字符終止的字符序列。
- stream -- 這是指向 FILE 對(duì)象的指針,該 FILE 對(duì)象標(biāo)識(shí)了要被寫入字符串的流。
作用:把字符串寫入到指定的流 stream 中,但不包括空字符。
返回值:該函數(shù)返回一個(gè)非負(fù)值,如果發(fā)生錯(cuò)誤則返回 EOF。
下面是fputs的具體使用方法:
#include<stdio.h> int main() { //打開(kāi)文件 FILE* pf = fopen("test.txt", "w"); if (pf == NULL) { perror(" fopen fail"); return 1; } fputs("hello betty", pf); //關(guān)閉文件 fclose(pf); pf = NULL;//防止野指針 return 0; }
演示結(jié)果:
(2) fgets函數(shù)
頭文件:#include<stdio.h>
聲明:char *fgets(char *str, int n, FILE *stream)
- str -- 這是指向一個(gè)字符數(shù)組的指針,該數(shù)組存儲(chǔ)了要讀取的字符串。
- n -- 這是要讀取的最大字符數(shù)(包括最后的空字符)。通常是使用以 str 傳遞的數(shù)組長(zhǎng)度。
- stream -- 這是指向 FILE 對(duì)象的指針,該 FILE 對(duì)象標(biāo)識(shí)了要從中讀取字符的流。
作用:從指定的流 stream 讀取一行,并把它存儲(chǔ)在 str 所指向的字符串內(nèi)。當(dāng)讀取 (n-1) 個(gè)字符時(shí),或者讀取到換行符時(shí),或者到達(dá)文件末尾時(shí),它會(huì)停止,具體視情況而定。
返回值:如果成功,該函數(shù)返回相同的 str 參數(shù)。如果到達(dá)文件末尾或者沒(méi)有讀取到任何字符,str 的內(nèi)容保持不變,并返回一個(gè)空指針。如果發(fā)生錯(cuò)誤,返回一個(gè)空指針。
下面是fgets的具體使用方法:
#include<stdio.h> int main() { //打開(kāi)文件 FILE* pf = fopen("test.txt", "r"); if (pf == NULL) { perror(" fopen fail"); return 1; } fputs("hello betty", pf); char arr[] = "##########"; fgets(arr, 5, pf); puts(arr); //關(guān)閉文件 fclose(pf); pf = NULL;//防止野指針 return 0; }
輸出結(jié)果:
雖然讀取五個(gè)字符,但是只會(huì)顯示四個(gè)字符,因?yàn)?code>'\0也會(huì)默認(rèn)添加進(jìn)去
4.3 格式化輸入輸出
(1) fprintf函數(shù)
頭文件:#include<stdio.h>
聲明:int fprintf(FILE *stream, const char *format, ...)
- stream -- 這是指向 FILE 對(duì)象的指針,該 FILE 對(duì)象標(biāo)識(shí)了流。
- format -- 這是 C 字符串,包含了要被寫入到流 stream 中的文本。它可以包含嵌入的 format 標(biāo)簽,format 標(biāo)簽可被隨后的附加參數(shù)中指定的值替換,并按需求進(jìn)行格式化。
作用:按照一定格式向輸出流輸出數(shù)據(jù)。
返回值:如果成功,則返回寫入的字符總數(shù),否則返回一個(gè)負(fù)數(shù)。
下面是fprintf的具體使用方法:
typedef struct student { char name[20]; int height; float score; }stu; int main() { stu s = { "beidi", 170, 95.0 }; //寫文件 FILE* pf = fopen("test.txt", "w"); if (pf == NULL) { perror(" fopen fail"); return 1; } fprintf(pf, "%s %d %f", s.name, s.height, s.score); //關(guān)閉文件 fclose(pf); pf = NULL;//防止野指針 return 0; }
(2) fscanf函數(shù)
頭文件:#include<stdio.h>
聲明:int fscanf(FILE *stream, const char *format, ...)
- stream -- 這是指向 FILE 對(duì)象的指針,該 FILE 對(duì)象標(biāo)識(shí)了流。
- format -- 這是 C 字符串,包含了以下各項(xiàng)中的一個(gè)或多個(gè):
空格字符
、非空格字符
和format
說(shuō)明符。
作用:按照一定格式從輸入流輸入數(shù)據(jù)。
返回值:如果成功,該函數(shù)返回成功匹配和賦值的個(gè)數(shù)。如果到達(dá)文件末尾或發(fā)生讀錯(cuò)誤,則返回 EOF。
下面是fscanf的具體使用方法:
typedef struct student { char name[20]; int height; float score; }stu; int main() { stu s = { "beidi", 170, 95.0 }; //寫文件 FILE* pf = fopen("test.txt", "r"); if (pf == NULL) { perror(" fopen fail"); return 1; } fscanf(pf, "%s %d %f", s.name, &(s.height), &(s.score)); printf("%s %d %f", s.name, s.height, s.score); //關(guān)閉文件 fclose(pf); pf = NULL;//防止野指針 return 0; }
4.4 二進(jìn)制輸入輸出
(1) fwrite函數(shù)
頭文件:#include<stdio.h>
聲明:size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)
- ptr -- 這是指向要被寫入的元素?cái)?shù)組的指針。
- size -- 這是要被寫入的每個(gè)元素的大小,以字節(jié)為單位。
- nmemb -- 這是元素的個(gè)數(shù),每個(gè)元素的大小為 size 字節(jié)。
- stream -- 這是指向 FILE 對(duì)象的指針,該 FILE 對(duì)象指定了一個(gè)輸出流。
作用:把 ptr 所指向的數(shù)組中的數(shù)據(jù)寫入到給定流 stream 中。
返回值:如果成功,該函數(shù)返回一個(gè) size_t 對(duì)象,表示元素的總數(shù),該對(duì)象是一個(gè)整型數(shù)據(jù)類型。如果該數(shù)字與 nmemb 參數(shù)不同,則會(huì)顯示一個(gè)錯(cuò)誤。
下面是fwrite的具體使用方法:
typedef struct student { char name[20]; int height; float score; }stu; int main() { stu s = { "beidi", 170, 95.0 }; //寫文件 FILE* pf = fopen("test.txt", "wb");//二進(jìn)制寫入 if (pf == NULL) { perror(" fopen fail"); return 1; } fwrite(&s, sizeof(s), 1, pf); //關(guān)閉文件 fclose(pf); pf = NULL;//防止野指針 return 0; }
二進(jìn)制數(shù)據(jù)正常人是無(wú)法看懂的,但是電腦能準(zhǔn)確識(shí)別
(2) fread函數(shù)
頭文件:#include<stdio.h>
聲明:size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream)
- ptr -- 這是指向帶有最小尺寸 size*nmemb 字節(jié)的內(nèi)存塊的指針。
- size -- 這是要讀取的每個(gè)元素的大小,以字節(jié)為單位。
- nmemb -- 這是元素的個(gè)數(shù),每個(gè)元素的大小為 size 字節(jié)。
- stream -- 這是指向 FILE 對(duì)象的指針,該 FILE 對(duì)象指定了一個(gè)輸入流。
作用:從給定流 stream 讀取數(shù)據(jù)到 ptr 所指向的數(shù)組中
返回值:成功讀取的元素總數(shù)會(huì)以 size_t 對(duì)象返回,size_t 對(duì)象是一個(gè)整型數(shù)據(jù)類型。如果總數(shù)與 nmemb 參數(shù)不同,則可能發(fā)生了一個(gè)錯(cuò)誤或者到達(dá)了文件末尾。
下面是fread的具體使用方法
typedef struct student { char name[20]; int height; float score; }stu; int main() { stu s = {0}; //寫文件 FILE* pf = fopen("test.txt", "rb");//二進(jìn)制寫出 if (pf == NULL) { perror(" fopen fail"); return 1; } fread(&s, sizeof(s), 1, pf); printf("%s %d %f", s.name, s. height, s.score); //關(guān)閉文件 fclose(pf); pf = NULL;//防止野指針 return 0; }
4.5 流輸入輸出
從前面我們知道在我們輸入輸出時(shí),默認(rèn)打開(kāi)**stdin - 標(biāo)準(zhǔn)輸入流 **, stdout - 標(biāo)準(zhǔn)輸出流, **stderr - 標(biāo)準(zhǔn)輸錯(cuò)誤 **。那我們可不可以利用流來(lái)輸入輸出呢?答案自然是可以的,下面是具體實(shí)例:
int main() { printf("輸入前:"); int ch = fgetc(stdin);//輸入 printf("輸入后:"); fputc(ch, stdout);//輸出 return 0; }
還有另外一種方法:
int main() { printf("輸入前:"); int ch = 0; fscanf(stdin, "%c", &ch); printf("輸入后:"); fprintf(stdout, "%c", ch); return 0; }
4.6 補(bǔ)充
(1) sprintf與sscanf
頭文件:#include<stdio.h>
聲明:int sprintf(char *str, const char *format, ...)
- str -- 這是指向一個(gè)字符數(shù)組的指針,該數(shù)組存儲(chǔ)了 C 字符串。
- format -- 這是字符串,包含了要被寫入到字符串 str 的文本。它可以包含嵌入的 format 標(biāo)簽,format 標(biāo)簽可被隨后的附加參數(shù)中指定的值替換,并按需求進(jìn)行格式化
作用:將格式化數(shù)據(jù)轉(zhuǎn)換為字符串
返回值:如果成功,則返回寫入的字符總數(shù),不包括字符串追加在字符串末尾的空字符。如果失敗,則返回一個(gè)負(fù)數(shù)。
頭文件:#include<stdio.h>
聲明:int sscanf(const char *str, const char *format, ...)
- str -- 這是 C 字符串,是函數(shù)檢索數(shù)據(jù)的源。
- format -- 這是 C 字符串,包含了以下各項(xiàng)中的一個(gè)或多個(gè):
空格字符
、非空格字符
和format
說(shuō)明符。
作用:將字符串按照一定格式轉(zhuǎn)換為格式化數(shù)據(jù)
返回值:如果成功,該函數(shù)返回成功匹配和賦值的個(gè)數(shù)。如果到達(dá)文件末尾或發(fā)生讀錯(cuò)誤,則返回 EOF。
下列展示了sprintf與sscanf的具體用法:
typedef struct student { char name[20]; int height; float score; }stu; int main() { char buf[100] = { 0 }; stu s = { "betty", 170, 95.0f }; stu tmp = { 0 }; //將這個(gè)結(jié)構(gòu)體的成員轉(zhuǎn)化為字符串 sprintf(buf, "%s %d %f", s.name, s.height, s.score); printf("%s\n", buf); //將這個(gè)字符串中內(nèi)容還原為一個(gè)結(jié)構(gòu)體數(shù)據(jù)呢 sscanf(buf, "%s %d %f", tmp.name, &(tmp.height), &(tmp.score)); printf("%s %d %f", tmp.name, tmp.height, tmp.score); return 0; }
(2) 對(duì)比
下表展示了scanf與printf,fscanf與fprintf,sscanf與sprintf之間的區(qū)別
函數(shù) | 功能 |
---|---|
scanf | 針對(duì)標(biāo)準(zhǔn)輸入(鍵盤)的格式化輸入函數(shù) |
printf | 針對(duì)標(biāo)準(zhǔn)輸入出(屏幕)的格式化輸出函數(shù) |
fscanf | 針對(duì)所以輸入流的格式化輸入函數(shù) |
fprintf | 針對(duì)所以輸出流的格式化輸出函數(shù) |
sscanf | 從一個(gè)字符串中讀取一個(gè)格式化數(shù)據(jù) |
sprintf | 把一個(gè)格式化數(shù)據(jù)轉(zhuǎn)換為字符串 |
5. 文本文件和二進(jìn)制文件
根據(jù)數(shù)據(jù)的組織形式,數(shù)據(jù)?件被稱為?本?件或者二進(jìn)制?件。
- 文本文件:以
ASCII字符
的形式存儲(chǔ)的?件 - 二進(jìn)制文件:數(shù)據(jù)在內(nèi)存中以
?進(jìn)制
的形式存儲(chǔ)的文件
?個(gè)數(shù)據(jù)在?件中是怎么存儲(chǔ)的呢?字符?律以ASCII形式存儲(chǔ),數(shù)值型數(shù)據(jù)既可以?ASCII形式存儲(chǔ),也可以使??進(jìn)制形式存儲(chǔ)。
如有整數(shù)10000,如果以ASCII碼的形式輸出到磁盤,則磁盤中占?5個(gè)字節(jié)(每個(gè)字符?個(gè)字節(jié)),??進(jìn)制形式輸出,則在磁盤上只占4個(gè)字節(jié)。
字符1的二進(jìn)制序列:00110001,字符0的二進(jìn)制序列:00110000
測(cè)試代碼:
int main() { int a = 10000; FILE* pf = fopen("test.txt", "wb"); fwrite(&a, 4, 1, pf);//二進(jìn)制的形式寫到文件中 fclose(pf); pf = NULL; return 0; }
右擊源文件,添加現(xiàn)有項(xiàng),將test.txt添加進(jìn)入
右擊test.txt文件,選擇打開(kāi)方式,選擇二進(jìn)制編輯器
10 27 00 00便是10000以小端存儲(chǔ)的十六進(jìn)制形式。
6. 文件的隨機(jī)讀寫
6.1 fseek函數(shù)
頭文件:#include<stdio.h>
聲明:int fseek(FILE *stream, long int offset, int whence)
- stream -- 這是指向 FILE 對(duì)象的指針,該 FILE 對(duì)象標(biāo)識(shí)了流。
- offset -- 這是相對(duì) whence 的偏移量,以字節(jié)為單位。
- whence -- 這是表示開(kāi)始添加偏移 offset 的位置。
作用:設(shè)置流 stream 的文件位置為給定的偏移 offset,參數(shù) offset 意味著從給定的 whence 位置查找的字節(jié)數(shù)。
返回值:如果成功,則該函數(shù)返回零,否則返回非零值。
whence偏移offset的三種位置:
常量 | 描述 |
---|---|
SEEK_SET | 文件的開(kāi)頭 |
SEEK_CUR | 文件指針的當(dāng)前位置 |
SEEK_END | 文件的末尾 |
假設(shè)文件中放的是字符串“abcdef,下面是fseek的具體使用實(shí)例:
int main() { //打開(kāi)文件 FILE* pf = fopen("test.txt", "r"); if (pf == NULL) { perror(" fopen fail"); return 1; } fseek(pf, 4, SEEK_SET); //從其實(shí)位置偏移四個(gè)字節(jié) int ch1 = fgetc(pf); printf("%c ", ch1); fseek(pf, -3, SEEK_END); //從結(jié)束位置偏移七個(gè)個(gè)字節(jié) int ch2 = fgetc(pf); printf("%c ", ch2); fseek(pf, 1, SEEK_CUR); //從當(dāng)前位置偏移一個(gè)字節(jié) int ch3 = fgetc(pf); printf("%c ", ch3); //關(guān)閉文件 fclose(pf); pf = NULL;//防止野指針 return 0; }
- 從起始位置偏移四個(gè)字節(jié),輸出e。
- 從末尾偏移三個(gè)字節(jié),輸出d。
- 此時(shí)偏移指向e的位置,再偏移一個(gè)字節(jié)指向f。
6.2 ftell函數(shù)
頭文件:#include<stdio.h>
聲明:long int ftell(FILE *stream)
stream -- 這是指向 FILE 對(duì)象的指針,該 FILE 對(duì)象標(biāo)識(shí)了流。
作用:返回?件指針相對(duì)于起始位置的偏移量
返回值:該函數(shù)返回位置標(biāo)識(shí)符的當(dāng)前值。如果發(fā)生錯(cuò)誤,則返回 -1L,全局變量 errno 被設(shè)置為一個(gè)正值。
我們可以利用fseek和ftell來(lái)計(jì)算文件的長(zhǎng)度(不包含'\0'),下列是代碼示例
int main() { FILE* pFile; long size; pFile = fopen("test.txt", "rb"); if (pFile == NULL) perror("Error opening file"); else { fseek(pFile, 0, SEEK_END); //non-portable size = ftell(pFile); fclose(pFile); printf("文件長(zhǎng)度為: %ld bytes.\n", size); } return 0; }
6.3 rewind函數(shù)
頭文件:#include<stdio.h>
聲明:void rewind(FILE *stream)
stream -- 這是指向 FILE 對(duì)象的指針,該 FILE 對(duì)象標(biāo)識(shí)了流
作用:讓?件指針的位置回到?件的起始位置
返回值:該函數(shù)不返回任何值。
rewind常常在文件讀與寫同時(shí)使用時(shí),以方便文件讀取。下面是rewind的具體使用實(shí)例:
#include <stdio.h> int main() { int n; FILE* pFile; char buffer[27]; pFile = fopen("myfile.txt", "w+"); for (n = 'A'; n <= 'Z'; n++) fputc(n, pFile);//放入26個(gè)字母 rewind(pFile);//回到起始位置,方便讀取 fread(buffer, 1, 26, pFile);//讀取· fclose(pFile); buffer[26] = '\0';//字符串的結(jié)束標(biāo)識(shí) printf(buffer); return 0; }
7. ?件讀取結(jié)束的判定
7.1 被錯(cuò)誤使用的 feof
在我們學(xué)習(xí)C語(yǔ)言文件操作的過(guò)程中,常常會(huì)有人誤認(rèn)為feof是判斷文件是否結(jié)束的函數(shù),其實(shí)這并不準(zhǔn)確。feof 的作?是:當(dāng)?件讀取結(jié)束的時(shí)候,判斷是讀取結(jié)束的原因是:遇到?件尾結(jié)束還是文件讀取失敗結(jié)束。
7.2 常見(jiàn)的結(jié)束標(biāo)志
函數(shù) | 結(jié)束標(biāo)志 |
---|---|
fgetc | 如果讀取正常,返回讀取到的字符的ASCLL碼值 如果讀取失敗,返回EOF |
fgets | 如果讀取正常,返回讀取到的數(shù)據(jù)的地址 如果讀取失敗,返回NULL |
fscanf | 如果讀取正常,返回的是格式串中指定的數(shù)據(jù)個(gè)數(shù) 如果讀取失敗,返回的是小于格式串中指定的數(shù)據(jù)個(gè)數(shù) |
fread | 如果讀取正常,返回的是等于要讀取的數(shù)據(jù)個(gè)數(shù) 如果讀取失敗,返回的是小于要讀取的數(shù)據(jù)個(gè)數(shù) |
8. 文件緩沖區(qū)
ANSIC 標(biāo)準(zhǔn)采用緩沖文件系統(tǒng)處理的數(shù)據(jù)文件的,所謂緩沖文件系統(tǒng)是指系統(tǒng)自動(dòng)地在內(nèi)存中為程序中每一個(gè)正在使用的文件開(kāi)辟一塊“文件緩沖區(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)決定的。
我們可以利用下列代碼證明緩沖區(qū)的存在:
include <stdio.h> #include <windows.h> //VS2019 WIN11環(huán)境測(cè)試 int main() { FILE* pf = fopen("test.txt", "w"); fputs("abcdef", pf); //先將代碼放在輸出緩沖區(qū) printf("睡眠10秒-已經(jīng)寫數(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ù)寫到?件(磁盤) //注: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; }
刷新緩沖區(qū)前:
刷新緩沖區(qū)后:
因?yàn)橛芯彌_區(qū)的存在,C語(yǔ)?在操作?件的時(shí)候,需要做刷新緩沖區(qū)或者在?件操作結(jié)束的時(shí)候關(guān)閉?件。如果不做,可能導(dǎo)致讀寫?件的問(wèn)題。
以上就是一文帶你掌握C語(yǔ)言中的文件操作的詳細(xì)內(nèi)容,更多關(guān)于C語(yǔ)言文件操作的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C語(yǔ)言中動(dòng)態(tài)內(nèi)存管理初學(xué)者容易犯的6個(gè)錯(cuò)誤分享
本篇文章主要介紹了初學(xué)者使用C語(yǔ)言中動(dòng)態(tài)內(nèi)存管理的4個(gè)函數(shù)時(shí)最容易犯的6個(gè)錯(cuò)誤,以及如何避免這些錯(cuò)誤,文中的示例代碼講解詳細(xì),感興趣的可以了解一下2023-04-04基于C語(yǔ)言構(gòu)建一個(gè)獨(dú)立棧協(xié)程和共享?xiàng)f(xié)程的任務(wù)調(diào)度系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了如何基于C語(yǔ)言構(gòu)建一個(gè)獨(dú)立棧協(xié)程和共享?xiàng)f(xié)程的任務(wù)調(diào)度系統(tǒng),文中的示例代碼講解詳細(xì),需要的可以參考下2024-02-02C++驗(yàn)證LeetCode包圍區(qū)域的DFS方法
這篇文章主要介紹了C++驗(yàn)證LeetCode包圍區(qū)域的DFS方法,本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-07-07算法學(xué)習(xí)入門之使用C語(yǔ)言實(shí)現(xiàn)各大基本的排序算法
這篇文章主要介紹了使用C語(yǔ)言實(shí)現(xiàn)各大基本的排序算法的方法,同時(shí)也對(duì)算法的選擇問(wèn)題上給出了一些建議,的朋友可以參考下2015-12-12C++ Opencv自寫函數(shù)實(shí)現(xiàn)膨脹腐蝕處理技巧
這篇文章主要介紹了C++ Opencv 自寫函數(shù)實(shí)現(xiàn)膨脹腐蝕處理,本文通過(guò)示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-10-10C語(yǔ)言實(shí)現(xiàn)靜態(tài)版通訊錄的示例代碼
這篇文章主要為大家詳細(xì)介紹了如何利用C語(yǔ)言實(shí)現(xiàn)一個(gè)簡(jiǎn)單的靜態(tài)版通訊錄,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)C語(yǔ)言有一定幫助,需要的可以參考一下2022-08-08解析c++ 中智能指針引用計(jì)數(shù)為什么不是0原理
這篇文章主要為大家介紹了C語(yǔ)言中智能指針引用計(jì)數(shù)為什么不是0原理解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08C++面試題之?dāng)?shù)a、b的值互換(不使用中間變量)
這篇文章主要介紹了不使用中間變量,C++實(shí)現(xiàn)數(shù)a、b的值互相轉(zhuǎn)換操作,感興趣的小伙伴們可以參考一下2016-07-07