🌈個人主頁:是店小二呀
🌈C語言筆記專欄:C語言筆記
🌈C++筆記專欄: C++筆記
🌈喜歡的詩句:無人扶我青云志 我自踏雪至山巔
🔥引言
本章將介紹文件讀取函數的相關知識和展示使用場景,通過這些函數就可以打開文件與我們之間的橋梁。
文章目錄
- 一、文件的打開和關閉
- 二、文件的打開模式
- 三、文件路徑
- 四、文件打開和關閉函數
- 4.1 fopen()
- 4.2 fclose()
- 五、順序讀寫函數
- 5.1 fputc()
- 5.2 fgetc()
- 5.3 實現文件拷貝
- 5.4 fputs()
- 5..5 fgets()
- 5.6 fscanf和fprintf函數
- 5.7 fprintf()
- 5.8 fscanf()
- 5.9 sprintf和sscanf函數
- 5.9.1 sprintf()
- 5.9.2 sscanf
- 小總結
- 六、二進制的方式讀寫函數
- 6.1 fwrite()
- 6.2 fread()
- 七、文件的隨機讀寫
- 7.1 fseek()
- 7.2 ftell()
- 7.3 rewind()
- 八、文件讀取結束的判斷
- 8.1 ferror()
- 8.2 feof()
- 小總結
- 檢查文件讀取結束場景
- 九、文件緩沖區
一、文件的打開和關閉
如果需要對文件進行一些讀寫操作,那么首先就需要先打開文件,在使用完以后關閉文件,所以最基本是打開文件和關閉文件。
ANSIC規定使用fopen函數來打開文件,fclose來關閉文件
打開文件FILE *fopen(const char *filename,const char *mode);
關閉文件FILE *fclose(FILE *stream);
二、文件的打開模式
mode表示文件的打開模式,下面都是文件的打開模式 :
?件使??式 | 含義 | 如果指定?件不存在 | |||
---|---|---|---|---|---|
“r”(只讀) | 為了輸?數據,打開?個已經存在的?本?件 | 出錯 | |||
“w”(只寫) | 為了輸出數據,打開?個?本?件 | 建??個新的?件 | |||
“a”(追加) | 向?本?件尾添加數據 | 建??個新的?件 | |||
“rb”(只讀) | 為了輸?數據,打開?個?進制?件 | 出錯 | |||
“wb”(只寫) | 為了輸出數據,打開?個?進制?件 | 建??個新的?件 | |||
“ab”(追加) | 向?個?進制?件尾添加數據 | 建??個新的?件 | |||
“r+”(讀寫) | 為了讀和寫,打開?個?本?件 | 出錯 | |||
“w+”(讀寫) | 為了讀和寫,建議?個新的?件 | 建??個新的?件 | |||
“a+”(讀寫) | 打開?個?件,在?件尾進?讀寫 | 建??個新的?件 | |||
“rb+”(讀寫) | 為了讀和寫打開?個?進制?件 | 出錯 | |||
“wb+”(讀寫) | 為了讀和寫,新建?個新的?進制?件 | 建??個新的?件 | |||
“ab+”(讀寫) | 打開?個?進制?件,在?件尾進?讀和寫 | 建??個新的?件 |
三、文件路徑
在打開和關閉文件一般直接輸入指定的文件名,當然也可以根據文件的路徑對該文件進行操作。
路徑分為:
- 絕對路徑
- 相對路徑
- . 表示當前路徑
- … 上一級路徑
對此當前文件test1.txt在D:\code\2024\2024\Document(絕對路徑)
FILE* pf = fopen("C:\\Users\\zpeng\\Desktop\\test.txt", "w"); 當test1.txt在其他文件夾中,可以通過上面表示的關系尋找
FILE* pf = fopen(".\\..\\..\\hehe\\test.txt", "w");
四、文件打開和關閉函數
4.1 fopen()
FILE *fopen(const char * filename, const char * mode)
【說明】:
- 功能:打開其名稱在參數 filename 中指定的文件,并將其與流相關聯,該流可在將來的操作中通過返回的
FILE指針
進行標識。 - 返回值:如果文件成功打開,該函數將返回指FILE對象的指針,該對象可用于在將來的操作中標識流,如果文件沒有成功打開,則將返回NULL。
- 允許對流執行的操作以及如何執行這些操作由 mode 參數定義
4.2 fclose()
int fclose(FLIE *stream)
【說明】:
- 功能:關閉與流關聯的文件并取消關聯
- 與流關聯的所有內部緩沖區都將與其解除關聯并刷新:寫入任何未寫入的輸出緩沖區的內容,并丟棄任何未寫入輸入緩沖區的內容。(更新緩沖區)
- 返回值:流關閉成功,則返回0。關閉失敗,返回EOF
五、順序讀寫函數
這里所有的輸入流、輸出流分別指stdin(標準輸入流)、stdout(標準輸出流)和文件流
5.1 fputc()
【函數部分說明】:
功能上:
將字符寫入流并推進位置指示器,然后自動前進1。適用于所有輸入流(字符輸入到流中)
參數部分上:
character:
- 將要寫入的字符的整型提升
- 寫入時,該值在內部轉換為無符號字符
stream:
- 指向標識輸出流的FILE對象指針
返回值:
- 成功,將返回寫入的字符。發生寫入錯誤,則返回EOF并設置錯誤指示符(ferror)
int main()
{FILE* pf = fopen("test1.txt", "w");if (pf == NULL){perror("fopen fail!!!");return;}fputc('d', pf);fputc('e', pf);fclose(pf);pf = NULL;return 0;
}
【使用說明】:
- 首次調用fputc函數將字符‘d’寫入到文件當中,位置指示器向前+1就會指向‘d’的下一個位置,第二次fputc函數時,字符‘e’在這個位置輸入進去。
- 注意這里對流執行寫操作"w"模式
- 對于stream可以是文件流也可以是標準流
【練習】:使用fputc將A ~Z個字符輸入到指定文件和顯示器中
int main()
{FILE* pf = fopen("test1.txt", "w");if (pf == NULL){perror("fopen fail!!!");return;}for (char i = 'A'; i <= 'Z'; i++){fputc(i, pf);//輸入到文件中fputc(i, stdout);//輸入到顯示器中}fclose(pf);pf = NULL;return 0;
}
5.2 fgetc()
【函數部分說明】:
- 功能:字符輸入函數,從流中獲得字符,返回指定流的內部文件位置指示符當前指向的字符,內部文件位置指示器將前進到下一個字符
- 返回值:成功后,將返回字符讀取(進行整型提升)。如果失敗則,返回EOF
int main()
{FILE* pf = fopen("test1.txt", "r");if (pf == NULL){perror("fopen fail!!!");return;} char ch;//ch用于接收fgetc從流中獲得的字符ch = fgetc(pf);printf("%c", ch);ch = fgetc(pf);printf("%c", ch);fclose(pf);pf = NULL;return 0;
}
【使用說明】:
- 這里對流的操作采用mode是"r"模式
- 此時在指定文件提前寫入數據,使用fgetc讀取該文中的數據,第一次使用時讀取‘h’字符,指示器++1,指向‘e’字符位置,在第二次使用讀取該字符。
- 當然也可以直接從鍵盤讀取數據(使用標準輸入流stdin)–>改為
ch = fgetc(stdin);
5.3 實現文件拷貝
通過以上兩個函數,我們可以用來實現文件拷貝的場景
int main()
{FILE* pfread = fopen("data1.txt", "r");if (pfread == NULL){perror("fopen fail!!!");return;} FILE* pfwrite = fopen("data2.txt", "w");if (pfwrite == NULL){fclose(pfread);//讀文件操作沒有必要pfread=NULL;perror("fopen fail!!!");return;}char ch;while ((ch = fgetc(pfread)) != EOF){fputc(ch, pfwrite);}fclose(pfread);fclose(pfwrite);return 0;
}
【使用說明】:
- 這里先提前準備好兩個文件,并且在
data1.txt
中提前輸入數據 - 這里需要對兩個不同文件使用不同mode打開,對
data1.txt
使用讀的形式,而data2.txt
是寫的形式打開 - 在while循環中一個從
data1.txt
讀取字符,同時一個向data2.txt
輸入字符,直到讀取完成或者發生錯誤
5.4 fputs()
【函數部分說明】:
- 功能:文本行輸入函數,將str指向的字符串寫入流中,從指定的地址開始復制,直到結束標志\0(\0’字符不會復制到流中)
- 返回值:成功,返回一個非負值,失敗,則返回EOF
int main()
{FILE * pf = fopen("test1.txt", "w");if (pf == NULL){perror("fopen fail!!!");return;} //fputs("hellow world", pf); -- 將字符串輸入到文件流中fputs("hellow world", stdout); -- 將字符串輸入到輸出流中(打印到屏幕上)fclose(pf);pf=NULL;return 0;
}
5…5 fgets()
【函數部分說明】:
功能上:
- 文本行輸入函數,從流中獲得字符串,直到讀取到
num-1
個字符或者在達到換行符或文件末尾(前者為準)
。 - 換行符(\n)會使
fgets
停止,但函數將其視為有效字符,并包含在復制到str
的字符串中,在將字符復制到str
之后,會自動添加\0
,導致只能讀取num-1
字符
參數部分上:
str:
- 指向復制字符串讀取到char數組的指針(這里提前開辟好數組)
num:
- 復制到str中的最大字符數(包括‘\0’字符-字符串)
返回值:
- 成功,返回str指針,失敗,返回
EOF
int main()
{FILE* pf = fopen("test1.txt", "r");if (pf == NULL){perror("fopen fail!!!");return;}char ch[1000]="xxxxxxx";fgets(ch, 7, pf);printf("%s", ch);fclose(pf);pf = NULL;return 0;
}
【說明】:給\0留了一個位置
5.6 fscanf和fprintf函數
fprint
和fscanf
屬于格式化輸入輸出函數,將不同數據類型放在結構體中進行統一管理。
struct S
{char a[1000];int p;float pa;
};
5.7 fprintf()
【函數部分說明】:
功能上:
- 按格式化數據寫入流,如果format包含格式說明符(%開頭的子序列),則格式化format后面的其他參數并將其插入到生成的字符串中,代替其各自的說明符.
- 根據格式說明符順序,匹配不同類型對應的數據,輸入到
text1.txt
中去。
返回值:
- 成功,返回寫入字符總數,失敗,則返回負數
int main()
{struct S s={"zhangsan",100,4.3};FILE* pf = fopen("test1.txt", "w");if (pf == NULL){perror("fopen fail!!!");return;}fprintf(pf,"%s %d %f", s.a, s.p, s.pa);fclose(pf);pf = NULL;return 0;
}
5.8 fscanf()
【函數部分說明】:
功能:
- 從流中讀取數據,并根據格式將其存儲到其他參數指向的位置。
- 該對象由格式化字符串的相應格式說明符指示。
- 根據格式說明符順序,匹配
text1.txt
對應的數據,輸入到對應的數據變量中
返回值:
成功后,該函數返回已成功填充的參數列表的項數,失敗,EOF
int main()
{struct S s;//這里沒有初始化,目的是通過fscanf賦值FILE* pf = fopen("test1.txt", "r");if (pf == NULL){perror("fopen fail!!!");return;} fscanf(pf, "%s %d %f", s.a, &(s.p), &(s.pa));printf("%s %d %f", s.a, s.p, s.pa);fclose(pf);pf = NULL;return 0;
}
5.9 sprintf和sscanf函數
5.9.1 sprintf()
【函數部分說明】:
功能:
從字符串中讀取格式化數據(將字符數組中的字符串按照格式說明符轉換為對應的格式化數據)
5.9.2 sscanf
【函數部分說明】:
功能:
將格式化數據寫入字符串(格式化數據轉換為字符串存放在字符數組中)
基于以上兩個函數使用場景:
int main()
{//將格式化的數據轉換為字符串存放在p數組中struct S s = { "zhangsan",100,4.3 };char p[1000] = { 0 };sprintf(p, "%s %d %f", s.a, s.p, s.pa);printf("%s\n", p);//從p這個字符串中提取格式化的數據(用p數組中的數據,為結構體t成員賦值)struct S t;sscanf(p, "%s %d %f", t.a, &(t.p), &(t.pa));printf("%s %d %lf", s.a, s.p, s.pa);return 0;
}
小總結
六、二進制的方式讀寫函數
6.1 fwrite()
在這里插入圖片描述
【函數部分說明】:
功能:
- 二進制輸出,將 count 元素數組(每個元素的大小為 size bytes)從 ptr 指向的內存塊寫入流中的當前位置
返回值:
- 成功將返回寫入元素個數
- 如果此數字與 count 參數不同,則寫入錯誤會阻止函數完成
- 如果 size 或 count 為零,則函數返回零,錯誤指示器保持不變。
int main()
{FILE* pf = fopen("test1.txt", "wb");//打開一個二級制文件if (pf == NULL){perror("fopen fail!!!");return;}int nums[] = { 1,2,3,4,5,6,7 };fwrite(nums, sizeof(int), 7, pf);fclose(pf);pf = NULL;return 0;
}
【注意】:這里是通過二進制的形式寫入文本文件中,如果想要看懂輸入的數據,可以在打開方式選擇二進制編輯器或者使用fread函數讀取出來
6.2 fread()
【函數部分說明】:
- 功能:
- 二進制輸入,從流中讀取 count 元素的數組,每個元素的大小為 bytes,并將它們存儲在 ptr 指定的內存塊中。
返回值:
- 成功將返回寫入實際元素個數
- 如果此數字與 count 參數不同,則表示讀取時發生讀取錯誤或達到文件末尾。
- 如果 size 或 count 為零,則該函數返回零, ptr 指向的流狀態和內容保持不變
比如:如果實際文件中只有7個字符,count是14,那么讀到7給字符就結束(返回值 < count就返回)
int main()
{FILE* pf = fopen("test1.txt", "rb");//打開一個二級制文件if (pf == NULL){perror("fopen fail!!!");return;}//int nums[] = { 1,2,3,4,5,6,7 };//fwrite(nums, sizeof(int), 7, pf);//讀取二進制存放的信息int nums[10] = { 0 };fread(nums, sizeof(int), 10, pf);for (int i = 0; i < 10; i++){printf("%d ", nums[i]);}fclose(pf);pf = NULL;return 0;
}
七、文件的隨機讀寫
由于文本文件和二進制文件使用方面存在差異,下面三個函數將采用二進制文件
7.1 fseek()
【函數部分說明】:
功能上:
- 重新定位流位置指示器,根據文件指針的位置和偏移量來定位文件指針
返回值:
- 成功,返回零,失敗,返回非零值
【注意】:
- 二進制文件:要從原點偏移的字節數
- 文本文件:零或 ftell 返回的值
【原點】
特別注意的是起源(從哪個位置),用作偏移參考位置
- SEEK_SET :文件開頭
- SEEK_CUR :文件指針的當前位置
- SEEK_END: 文件末尾
int main()
{FILE* pf = fopen("test1.txt", "wb");//打開一個二級制文件if (pf == NULL){perror("fopen fail!!!");return;}fputs("This is an apple", pf);fseek(pf, 3, SEEK_SET);fputs("sam", pf);fseek(pf, -3, SEEK_END);//文件指針往前走fputs("xxx", pf);fclosereturn 0;
}
7.2 ftell()
【函數部分說明】:
功能上:
- 返回流的位置指示器的當前值(返回文件指針相對于起始位置的偏移量 )
返回值:
- 對于二進制流,這是從文件開頭開始的字節
- 對于文本流,數值可能沒有意義
int main()
{FILE* pf = fopen("test1.txt", "wb");//打開一個二級制文件if (pf == NULL){perror("fopen fail!!!");return;}fputs("This is an apple", pf);fseek(pf, 0, SEEK_END);int size = ftell(pf);printf("%d", size);//size==16,源頭到參考位置的字節數fclose(pf);pf = NULL;return 0;
}
7.3 rewind()
【函數部分說明】:
- 功能:讓文件指針的位置回到文件的起始位置
int main()
{FILE* pf = fopen("test1.txt", "wb");//打開一個二級制文件if (pf == NULL){perror("fopen fail!!!");return;}fputs("This is an apple", pf);fseek(pf, 0, SEEK_END);rewind(pf);int size = ftell(pf);printf("%d", size);fclose(pf);pf = NULL;return 0;
}
八、文件讀取結束的判斷
【牢記】:在文件讀取過程中,不能用feof函數的返回值直接判斷文件是否結束
8.1 ferror()
【函數部分說明】:
- 功能:檢查錯誤指示器
- 返回值:檢查是否設置了與流關聯的錯誤指示器,如果設置了,則返回一個不同于零的值
8.2 feof()
【函數部分說明】:
-
功能:檢查文件結束指示器
-
返回值:**如果設置了與流關聯的文件結束指示符,則返回非零值。**否則,返回零。
小總結
文本文件讀取是否結束:判斷返回值是否為EOF(fgetc)或者NULL(fgets)
二進制文件讀取是否結束:判斷返回值是否小于實際要讀的個數。
檢查文件讀取結束場景
while ((c = fgetc(fp)) != EOF) // 標準C I/O讀取
{putchar(c);
}//判斷是什么原因結束的if (ferror(fp))
puts("I/O error when reading");else if (feof(fp))
puts("End of file reached successfully");
九、文件緩沖區
ANSIC
標準采?“緩沖文件系統”處理的數據?件的,所謂緩沖?件系統是指系統?動地在內存中為程序中每?個正在使?的?件開辟?塊“?件緩沖區”。從內存向磁盤輸出數據會先送到內存中的緩沖區,裝滿緩沖區后才?起送到磁盤上。如果從磁盤向計算機讀?數據,則從磁盤?件中讀取數據輸?到內存緩沖區(充滿緩沖區),然后再從緩沖區逐個地將數據送到程序數據區(程序變量等)。緩沖區的??根據C編譯系統決定的.上述過程轉換為下圖展示。
【驗證】:
int main()
{
FILE*pf = fopen("test.txt", "w");
fputs("abcdef", pf);//先將代碼放在輸出緩沖區
printf("睡眠10秒-已經寫數據了,打開test.txt?件,發現?件沒有內容\n");
Sleep(10000);
printf("刷新緩沖區\n");
fflush(pf);//刷新緩沖區時,才將輸出緩沖區的數據寫到?件(磁盤)
//注:fflush 在?版本的VS上不能使?了
printf("再睡眠10秒-此時,再次打開test.txt?件,?件有內容了\n");
Sleep(10000);
fclose(pf);
//注:fclose在關閉?件的時候,也會刷新緩沖區
pf = NULL;
return 0;
}
【目的】:
程序延遲執行時間內,檢查文本編輯器是否存在數據,等待程序睡眠時間過去,再次檢查文本編輯器是否存在數據,驗證內存和外存之間存在緩沖區
【結論】:
因為有緩沖區的存在,C語?在操作文件的時候,需要做刷新緩沖區或者在?件操作結束的時候關閉文件。如果不做,可能導致讀寫?件的問題
以上就是本篇文章的所有內容,在此感謝大家的觀看!這里是店小二C語言筆記,希望對你在學習C語言中有所幫助!