?? 歡迎大家來到貝蒂大講堂??
????養(yǎng)成好習(xí)慣,先贊后看哦~????
所屬專欄:C語言學(xué)習(xí)
貝蒂的主頁:Betty‘s blog
1. 什么是文件
文件其實是指一組相關(guān)數(shù)據(jù)的有序集合。這個數(shù)據(jù)集有一個名稱,叫做文件名。文件通常是駐留在外部介質(zhì)(如磁盤等)上的,在使用時才調(diào)入內(nèi)存中來。
從文件功能上來講,一般可分為:程序文件與數(shù)據(jù)文件
1.1 文件名
一個文件要有一個唯一的文件標(biāo)識,以便用戶識別和引用,這就是
文件名
- ?件名包含3部分:?件路徑+?件名主?+?件后綴
- 例如:
c:\code\test.txt
1.2 程序文件
程序文件一般指:
源程序文件(后綴為.c)
,目標(biāo)文件(windows環(huán)境后綴為.obj)
,可執(zhí)行程序(windows環(huán)境后綴為.exe)
- 源程序文件一般在創(chuàng)建程序目錄文件下
- 源程序文件經(jīng)過編譯器鏈接與鏈接器鏈接可以生成我們的可執(zhí)行程序的文件。
1.3 數(shù)據(jù)文件
?件的內(nèi)容不?定是程序,?是
程序運行時讀寫的數(shù)據(jù)
,?如程序運?需要從中讀取數(shù)據(jù)的?件,或者輸出內(nèi)容的?件。
本章討論的是數(shù)據(jù)文件。
在以前各章所處理數(shù)據(jù)的輸?輸出都是以終端為對象的,即從終端的鍵盤輸?數(shù)據(jù),運?結(jié)果顯?到顯?器上。
其實有時候我們會把信息輸出到磁盤上,當(dāng)需要的時候再從磁盤上把數(shù)據(jù)讀取到內(nèi)存中使?,這?處理的就是磁盤上?件。
2. 文件的作用
如果沒有?件,我們寫的程序的數(shù)據(jù)是存儲在電腦的內(nèi)存中,如果程序退出,內(nèi)存回收,數(shù)據(jù)就丟失了,等再次運?程序,是看不到上次程序的數(shù)據(jù)的,如果要將數(shù)據(jù)進?持久化的保存,我們就需要使?文件。
3. 文件的打開與關(guān)閉
3.1 流與標(biāo)準(zhǔn)流
(1) 流
我們程序的數(shù)據(jù)需要輸出到各種外部設(shè)備,也需要從外部設(shè)備獲取數(shù)據(jù),不同的外部設(shè)備的輸?輸出操作各不相同,為了?便程序員對各種設(shè)備進??便的操作,我們抽象出了流的概念,我們可以把流想象成流淌著字符的河。
C程序針對文件
、畫面
、 鍵盤
等的數(shù)據(jù)輸?輸出操作都是通過流操作的。?般情況下,我們要想向流?寫數(shù)據(jù),或者從流中讀取數(shù)據(jù),都是要打開流,然后操作。
(2) 標(biāo)準(zhǔn)流
那為什么我們從鍵盤輸?數(shù)據(jù),向屏幕上輸出數(shù)據(jù),并沒有打開流呢?那是因為C語?程序在啟動的時候,默認打開了3個流:
? 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)錯誤流,?多數(shù)環(huán)境中輸出到顯?器界?。
這是默認打開了這三個流,我們使?scanf、printf等函數(shù)就可以直接進?輸?輸出操作的。stdin、stdout、stderr三個流的類型是: FILE*
,通常稱為文件指針。
在C語?中,就是通過 FILE*
的?件指針來維護流的各種操作的。
3.2 文件指針
在緩沖文件系統(tǒng)中,關(guān)鍵的概念是“文件類型指針”,簡稱“文件指針”。
每個被使用的文件都在內(nèi)存中開辟了一個相應(yīng)的文件信息區(qū),用來存放文件的相關(guān)信息(如文件的名字,文件狀態(tài)及文件當(dāng)前的位置等)。這些信息是保存在一個結(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)打開一個文件的時候,系統(tǒng)會根據(jù)文件的情況自動創(chuàng)建一個FILE結(jié)構(gòu)的變量,并填充其中的信息,使用者不必關(guān)心細節(jié)。
一般都是通過一個FILE的指針來維護這個FILE結(jié)構(gòu)的變量,這樣使用起來更加方便。我們來看看如何創(chuàng)建一個FILE的指針變量
FILE* pf; //文件指針變量
- 定義pf是一個指向FILE類型數(shù)據(jù)的指針變量??梢允筽f指向某個文件的文件信息區(qū)(是一個結(jié)構(gòu)體變量)。通過該文件信息區(qū)中的信息就能夠訪問該文件。也就是說,通過文件指針變量能夠找到與它關(guān)聯(lián)的文件,以此來進行相關(guān)操作。
3.3 文件的打開與關(guān)閉
?件在讀寫之前應(yīng)該先打開?件,在使?結(jié)束之后應(yīng)該關(guān)閉?件。這與我們前面學(xué)習(xí)的動態(tài)內(nèi)存開辟很類似。
在編寫程序的時候,在打開?件的同時,都會返回?個FILE*
的指針變量指向該?件,也相當(dāng)于建?了指針和?件的關(guān)系。
ANSIC規(guī)定使? fopen
函數(shù)來打開?件, fclose
來關(guān)閉?件。
(1) fopen函數(shù)
頭文件#include<stdio.h>
聲明:FILE *fopen(const char *filename, const char *mode)
filename -- 字符串,表示要打開的文件名稱。
mode -- 字符串,表示文件的訪問模式。
作用:使用給定的模式 mode 打開 filename 所指向的文件
返回值:該函數(shù)返回一個 FILE 指針。否則返回 NULL,且設(shè)置全局變量 errno 來標(biāo)識錯誤。
下表為常見的訪問模式(mode):
文件使用方式 | 含義 | 如果指定文件不存在 |
---|---|---|
“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+”(讀寫) | 為了讀和寫,新建一個新的二進制文件 | 建立一個新的文件 |
“a+”(讀寫) | 打開一個二進制文件,在文件尾進行讀寫 | 建立一個新的文件 |
(2) fclose函數(shù)
頭文件#include<stdio.h>
聲明:int fclose(FILE *stream)
- stream -- 這是指向 FILE 對象的指針,該 FILE 對象指定了要被關(guān)閉的流。
作用:關(guān)閉流 stream。刷新所有的緩沖區(qū)
返回值:如果流成功關(guān)閉,則該方法返回零。如果失敗,則返回 EOF。
下列是fopen與fclose具體使用:
int main()
{
//打開文件
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 -- 這是要被寫入的字符。該字符以其對應(yīng)的 int 值進行傳遞。
- stream -- 這是指向 FILE 對象的指針,該 FILE 對象標(biāo)識了要被寫入字符的流。
作用:把參數(shù) char 指定的字符(一個無符號字符)寫入到指定的流 stream 中。
返回值:如果沒有發(fā)生錯誤,則返回被寫入的字符。如果發(fā)生錯誤,則返回 EOF,并設(shè)置錯誤標(biāo)識符。
下列是具體的fputc的使用方法:
#include<stdio.h>
int main()
{
//打開文件
FILE* pf = fopen("test.txt", "w");
if (pf == NULL)
{
perror(" fopen fail");
return 1;
}
//將abc放進文件
fputc('a', pf);
fputc('b', pf);
fputc('c', pf);
//關(guān)閉文件
fclose(pf);
pf = NULL;//防止野指針
return 0;
}
如果你想查看寫入結(jié)果,可以在創(chuàng)建項目下找到Debug文件,打開
(2) fgetc函數(shù)
頭文件:#include<stdio.h>
聲明:int fgetc(FILE *stream)
- stream -- 這是指向 FILE 對象的指針,該 FILE 對象標(biāo)識了要在上面執(zhí)行操作的流。
作用:從指定的流 stream 獲取下一個字符(一個無符號字符)。
返回值:該函數(shù)以無符號 char 強制轉(zhuǎn)換為 int 的形式返回讀取的字符,如果到達文件末尾或發(fā)生讀錯誤,則返回 EOF。
下列是具體的fputc的使用方法:
#include<stdio.h>
int main()
{
//打開文件
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("讀出來的字符為:%c\n", ch);
ch = fgetc(pf);
printf("讀出來的字符為:%c\n", ch);
ch = fgetc(pf);
printf("讀出來的字符為:%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 -- 這是一個數(shù)組,包含了要寫入的以空字符終止的字符序列。
stream -- 這是指向 FILE 對象的指針,該 FILE 對象標(biāo)識了要被寫入字符串的流。
作用:把字符串寫入到指定的流 stream 中,但不包括空字符。
返回值:該函數(shù)返回一個非負值,如果發(fā)生錯誤則返回 EOF。
下面是fputs的具體使用方法:
#include<stdio.h>
int main()
{
//打開文件
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 -- 這是指向一個字符數(shù)組的指針,該數(shù)組存儲了要讀取的字符串。
- n -- 這是要讀取的最大字符數(shù)(包括最后的空字符)。通常是使用以 str 傳遞的數(shù)組長度。
- stream -- 這是指向 FILE 對象的指針,該 FILE 對象標(biāo)識了要從中讀取字符的流。
作用:從指定的流 stream 讀取一行,并把它存儲在 str 所指向的字符串內(nèi)。當(dāng)讀取 (n-1) 個字符時,或者讀取到換行符時,或者到達文件末尾時,它會停止,具體視情況而定。
返回值:如果成功,該函數(shù)返回相同的 str 參數(shù)。如果到達文件末尾或者沒有讀取到任何字符,str 的內(nèi)容保持不變,并返回一個空指針。如果發(fā)生錯誤,返回一個空指針。
下面是fgets的具體使用方法:
#include<stdio.h>
int main()
{
//打開文件
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é)果:
- 雖然讀取五個字符,但是只會顯示四個字符,因為
'\0
也會默認添加進去
4.3 格式化輸入輸出
(1) fprintf函數(shù)
頭文件:#include<stdio.h>
聲明:int fprintf(FILE *stream, const char *format, ...)
- stream -- 這是指向 FILE 對象的指針,該 FILE 對象標(biāo)識了流。
- format -- 這是 C 字符串,包含了要被寫入到流 stream 中的文本。它可以包含嵌入的 format 標(biāo)簽,format 標(biāo)簽可被隨后的附加參數(shù)中指定的值替換,并按需求進行格式化。
作用:按照一定格式向輸出流輸出數(shù)據(jù)。
返回值:如果成功,則返回寫入的字符總數(shù),否則返回一個負數(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 對象的指針,該 FILE 對象標(biāo)識了流。
- format -- 這是 C 字符串,包含了以下各項中的一個或多個:
空格字符
、非空格字符
和format
說明符。- 作用:按照一定格式從輸入流輸入數(shù)據(jù)。
- 返回值:如果成功,該函數(shù)返回成功匹配和賦值的個數(shù)。如果到達文件末尾或發(fā)生讀錯誤,則返回 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 二進制輸入輸出
(1) fwrite函數(shù)
- 頭文件:#include<stdio.h>
- 聲明:size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)
- ptr -- 這是指向要被寫入的元素數(shù)組的指針。
- size -- 這是要被寫入的每個元素的大小,以字節(jié)為單位。
- nmemb -- 這是元素的個數(shù),每個元素的大小為 size 字節(jié)。
- stream -- 這是指向 FILE 對象的指針,該 FILE 對象指定了一個輸出流。
- 作用:把 ptr 所指向的數(shù)組中的數(shù)據(jù)寫入到給定流 stream 中。
- 返回值:如果成功,該函數(shù)返回一個 size_t 對象,表示元素的總數(shù),該對象是一個整型數(shù)據(jù)類型。如果該數(shù)字與 nmemb 參數(shù)不同,則會顯示一個錯誤。
下面是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");//二進制寫入
if (pf == NULL)
{
perror(" fopen fail");
return 1;
}
fwrite(&s, sizeof(s), 1, pf);
//關(guān)閉文件
fclose(pf);
pf = NULL;//防止野指針
return 0;
}
- 二進制數(shù)據(jù)正常人是無法看懂的,但是電腦能準(zhǔn)確識別
(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 -- 這是要讀取的每個元素的大小,以字節(jié)為單位。
- nmemb -- 這是元素的個數(shù),每個元素的大小為 size 字節(jié)。
- stream -- 這是指向 FILE 對象的指針,該 FILE 對象指定了一個輸入流。
- 作用:從給定流 stream 讀取數(shù)據(jù)到 ptr 所指向的數(shù)組中
- 返回值:成功讀取的元素總數(shù)會以 size_t 對象返回,size_t 對象是一個整型數(shù)據(jù)類型。如果總數(shù)與 nmemb 參數(shù)不同,則可能發(fā)生了一個錯誤或者到達了文件末尾。
下面是fread的具體使用方法
typedef struct student {
char name[20];
int height;
float score;
}stu;
int main()
{
stu s = {0};
//寫文件
FILE* pf = fopen("test.txt", "rb");//二進制寫出
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 流輸入輸出
從前面我們知道在我們輸入輸出時,默認打開**stdin - 標(biāo)準(zhǔn)輸入流 **, stdout - 標(biāo)準(zhǔn)輸出流, **stderr - 標(biāo)準(zhǔn)輸錯誤 **。那我們可不可以利用流來輸入輸出呢?答案自然是可以的,下面是具體實例:
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 補充
(1) sprintf與sscanf
- 頭文件:#include<stdio.h>
- 聲明:int sprintf(char *str, const char *format, ...)
- str -- 這是指向一個字符數(shù)組的指針,該數(shù)組存儲了 C 字符串。
- format -- 這是字符串,包含了要被寫入到字符串 str 的文本。它可以包含嵌入的 format 標(biāo)簽,format 標(biāo)簽可被隨后的附加參數(shù)中指定的值替換,并按需求進行格式化
- 作用:將格式化數(shù)據(jù)轉(zhuǎn)換為字符串
- 返回值:如果成功,則返回寫入的字符總數(shù),不包括字符串追加在字符串末尾的空字符。如果失敗,則返回一個負數(shù)。
- 頭文件:#include<stdio.h>
- 聲明:int sscanf(const char *str, const char *format, ...)
- str -- 這是 C 字符串,是函數(shù)檢索數(shù)據(jù)的源。
- format -- 這是 C 字符串,包含了以下各項中的一個或多個:
空格字符
、非空格字符
和format
說明符。- 作用:將字符串按照一定格式轉(zhuǎn)換為格式化數(shù)據(jù)
- 返回值:如果成功,該函數(shù)返回成功匹配和賦值的個數(shù)。如果到達文件末尾或發(fā)生讀錯誤,則返回 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 };
//將這個結(jié)構(gòu)體的成員轉(zhuǎn)化為字符串
sprintf(buf, "%s %d %f", s.name, s.height, s.score);
printf("%s\n", buf);
//將這個字符串中內(nèi)容還原為一個結(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) 對比
下表展示了scanf與printf,fscanf與fprintf,sscanf與sprintf之間的區(qū)別
函數(shù) | 功能 |
---|---|
scanf | 針對標(biāo)準(zhǔn)輸入(鍵盤)的格式化輸入函數(shù) |
printf | 針對標(biāo)準(zhǔn)輸入出(屏幕)的格式化輸出函數(shù) |
fscanf | 針對所以輸入流的格式化輸入函數(shù) |
fprintf | 針對所以輸出流的格式化輸出函數(shù) |
sscanf | 從一個字符串中讀取一個格式化數(shù)據(jù) |
sprintf | 把一個格式化數(shù)據(jù)轉(zhuǎn)換為字符串 |
5. 文本文件和二進制文件
根據(jù)數(shù)據(jù)的組織形式,數(shù)據(jù)?件被稱為?本?件或者二進制?件。
- 文本文件:以
ASCII字符
的形式存儲的?件 - 二進制文件:數(shù)據(jù)在內(nèi)存中以
?進制
的形式存儲的文件
?個數(shù)據(jù)在?件中是怎么存儲的呢?字符?律以ASCII形式存儲,數(shù)值型數(shù)據(jù)既可以?ASCII形式存儲,也可以使??進制形式存儲。
如有整數(shù)10000,如果以ASCII碼的形式輸出到磁盤,則磁盤中占?5個字節(jié)(每個字符?個字節(jié)),??進制形式輸出,則在磁盤上只占4個字節(jié)。
- 字符1的二進制序列:00110001,字符0的二進制序列:00110000
測試代碼:
int main()
{
int a = 10000;
FILE* pf = fopen("test.txt", "wb");
fwrite(&a, 4, 1, pf);//二進制的形式寫到文件中
fclose(pf);
pf = NULL;
return 0;
}
- 右擊源文件,添加現(xiàn)有項,將test.txt添加進入
- 右擊test.txt文件,選擇打開方式,選擇二進制編輯器
- 10 27 00 00便是10000以小端存儲的十六進制形式。
6. 文件的隨機讀寫
6.1 fseek函數(shù)
- 頭文件:#include<stdio.h>
- 聲明:int fseek(FILE *stream, long int offset, int whence)
- stream -- 這是指向 FILE 對象的指針,該 FILE 對象標(biāo)識了流。
- offset -- 這是相對 whence 的偏移量,以字節(jié)為單位。
- whence -- 這是表示開始添加偏移 offset 的位置。
- 作用:設(shè)置流 stream 的文件位置為給定的偏移 offset,參數(shù) offset 意味著從給定的 whence 位置查找的字節(jié)數(shù)。
- 返回值:如果成功,則該函數(shù)返回零,否則返回非零值。
- whence偏移offset的三種位置:
常量 | 描述 |
---|---|
SEEK_SET | 文件的開頭 |
SEEK_CUR | 文件指針的當(dāng)前位置 |
SEEK_END | 文件的末尾 |
假設(shè)文件中放的是字符串“abcdef,下面是fseek的具體使用實例:
int main()
{
//打開文件
FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{
perror(" fopen fail");
return 1;
}
fseek(pf, 4, SEEK_SET);
//從其實位置偏移四個字節(jié)
int ch1 = fgetc(pf);
printf("%c ", ch1);
fseek(pf, -3, SEEK_END);
//從結(jié)束位置偏移七個個字節(jié)
int ch2 = fgetc(pf);
printf("%c ", ch2);
fseek(pf, 1, SEEK_CUR);
//從當(dāng)前位置偏移一個字節(jié)
int ch3 = fgetc(pf);
printf("%c ", ch3);
//關(guān)閉文件
fclose(pf);
pf = NULL;//防止野指針
return 0;
}
- 從起始位置偏移四個字節(jié),輸出e。
- 從末尾偏移三個字節(jié),輸出d。
- 此時偏移指向e的位置,再偏移一個字節(jié)指向f。
6.2 ftell函數(shù)
- 頭文件:#include<stdio.h>
- 聲明:long int ftell(FILE *stream)
- stream -- 這是指向 FILE 對象的指針,該 FILE 對象標(biāo)識了流。
- 作用:返回?件指針相對于起始位置的偏移量
- 返回值:該函數(shù)返回位置標(biāo)識符的當(dāng)前值。如果發(fā)生錯誤,則返回 -1L,全局變量 errno 被設(shè)置為一個正值。
我們可以利用fseek和ftell來計算文件的長度(不包含'\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("文件長度為: %ld bytes.\n", size);
}
return 0;
}
6.3 rewind函數(shù)
- 頭文件:#include<stdio.h>
- 聲明:void rewind(FILE *stream)
- stream -- 這是指向 FILE 對象的指針,該 FILE 對象標(biāo)識了流
- 作用:讓?件指針的位置回到?件的起始位置
- 返回值:該函數(shù)不返回任何值。
rewind常常在文件讀與寫同時使用時,以方便文件讀取。下面是rewind的具體使用實例:
#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個字母
rewind(pFile);//回到起始位置,方便讀取
fread(buffer, 1, 26, pFile);//讀取·
fclose(pFile);
buffer[26] = '\0';//字符串的結(jié)束標(biāo)識
printf(buffer);
return 0;
}
7. ?件讀取結(jié)束的判定
7.1 被錯誤使用的 feof
在我們學(xué)習(xí)C語言文件操作的過程中,常常會有人誤認為feof是判斷文件是否結(jié)束的函數(shù),其實這并不準(zhǔn)確。feof 的作?是:當(dāng)?件讀取結(jié)束的時候,判斷是讀取結(jié)束的原因是:遇到?件尾結(jié)束還是文件讀取失敗結(jié)束。
7.2 常見的結(jié)束標(biāo)志
函數(shù) | 結(jié)束標(biāo)志 |
---|---|
fgetc | 如果讀取正常,返回讀取到的字符的ASCLL碼值 如果讀取失敗,返回EOF |
fgets | 如果讀取正常,返回讀取到的數(shù)據(jù)的地址 如果讀取失敗,返回NULL |
fscanf | 如果讀取正常,返回的是格式串中指定的數(shù)據(jù)個數(shù) 如果讀取失敗,返回的是小于格式串中指定的數(shù)據(jù)個數(shù) |
fread | 如果讀取正常,返回的是等于要讀取的數(shù)據(jù)個數(shù) 如果讀取失敗,返回的是小于要讀取的數(shù)據(jù)個數(shù) |
8. 文件緩沖區(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)決定的。
我們可以利用下列代碼證明緩沖區(qū)的存在:
include <stdio.h>
#include <windows.h>
//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ū)后:文章來源:http://www.zghlxwxcb.cn/news/detail-825077.html
文章來源地址http://www.zghlxwxcb.cn/news/detail-825077.html
- 因為有緩沖區(qū)的存在,C語?在操作?件的時候,需要做刷新緩沖區(qū)或者在?件操作結(jié)束的時候關(guān)閉?件。如果不做,可能導(dǎo)致讀寫?件的問題。
到了這里,關(guān)于掌握C語言文件操作:從入門到精通的完整指南!的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!