檔案概述
檔案幾乎無處不在,主要分為磁碟檔案和裝置檔案,典型的磁碟檔案有文字檔案和二進位制檔案,磁碟檔案儲存在外部儲存介質(例如磁碟,硬碟,隨身碟等等)需要載入到記憶體中才能使用。
無論是文字檔案還是二進位制檔案在計算機內部都是以位元組為單位,二進位制的方式儲存。
文字檔案儲存的是字元對應的ASCII碼值,例如文字字元的a在計算機內部就是以二進位制01100001 即97儲存。當使用文字編輯器(例如Visual Studio Code)開啟文字檔案時,會將文字檔案的二進位制轉換成ASCII編碼對應的字元。
二進位制檔案存和取的是二進位制數值,常見的二進位制檔案有圖片、音訊、影片等等。二進位制檔案不能使用文字編輯器(例如Visual Studio Code)開啟,圖片需要使用特定的軟體(例如Windows的照片瀏覽器)開啟。
裝置檔案有標準輸入檔案(stdin),標準輸出檔案(stdout),標準錯誤(stderr)三個檔案,裝置檔案在程式啟動時由系統預設開啟,程式設計師無需呼叫
fopen(stdin/stdout/stderr,“r+”)
函式開啟這三個裝置檔案即可使用。
stdin: 標準輸入,預設為當前終端(鍵盤),我們使用的scanf、getchar函式預設從此終端獲得資料。
stdout:標準輸出,預設為當前終端(螢幕),我們使用的printf、puts函式預設輸出資訊到此終端。
stderr:標準出錯,預設為當前終端(螢幕),我們使用的perror函式預設輸出資訊到此終端。
檔案流指標
當開啟檔案時,系統核心會返回一個FILE結構體,該結構體有對檔案操作的所有資訊。
typedef struct{ short level; //緩衝區“滿”或者“空”的程度 unsigned flags; //檔案狀態標誌 char fd; //檔案描述符 unsigned char hold; //如無緩衝區不讀取字元 short bsize; //緩衝區的大小 unsigned char *buffer;//資料緩衝區的位置 unsigned ar; //指標,當前的指向 unsigned istemp; //臨時檔案,指示器 short token; //用於有效性的檢查 }FILE;
而當呼叫fopen()函式時,系統會返回這個FILE結構體的地址,即
FILE * p;
,當使用函式fputc(‘a’,p)往檔案寫入字元時,只需要傳遞檔案流指標p即可。
檔案流指標和之前的指標不同的是無法透過
*p
獲取檔案內容。
檔案的開啟和關閉
檔案的開啟
C語言提供了fopen()函式用於開啟檔案,該函式的宣告位於stdio。h標頭檔案中,fopen()呼叫時需要兩個引數:路徑名和開啟的方式,如果是當前路徑以。/表示,Visual Studio2019 本地Windows偵錯程式執行時原始檔file_open_close。c的當前目錄是D:\workspace\c\ittimelinedotnet\vs2019\c-core\c-core-advanced,開啟方式可以是如下幾種方式
r或rb 以只讀方式開啟一個文字檔案(不建立檔案,若檔案不存在則報錯)
w或wb 以寫方式開啟檔案(如果檔案存在則清空檔案,檔案不存在則建立一個檔案)
a或ab 以追加方式開啟檔案,在末尾新增內容,若檔案不存在則建立檔案
r+或rb+ 以可讀、可寫的方式開啟檔案(不建立新檔案)
w+或wb+ 以可讀、可寫的方式開啟檔案(如果檔案存在則清空檔案,檔案不存在則建立一個檔案)
a+或ab+ 以新增方式開啟檔案,開啟檔案並在末尾更改檔案,若檔案不存在則建立檔案
其中w選項如果檔案不存在會建立檔案,但是如果檔案有內容會清空。r選項檔案不存在,則不建立檔案。而b表示開啟二進位制檔案。
fopen()開啟檔案成功後返回
FILE*p
結構體的地址,也標識了那個檔案,操作指標就是操作那個檔案。開啟失敗返回NULL。
以只讀的方式開啟當前路徑下的test。txt檔案
#define _CRT_SECURE_NO_WARNINGS#include #include /* 以只讀方式開啟當前目錄的test。txt文字檔案*/void readonly_open_txt() { FILE* p_readonly = fopen(“。/test。txt”, “r”); if (p_readonly == NULL) { //追加錯誤提示字首 perror(“readonly mode open file”); return; }}/* 檔案的開啟和關閉 fopen()函式開啟方式說明 r或rb 以只讀方式開啟一個文字檔案(不建立檔案,若檔案不存在則報錯) w或wb 以寫方式開啟檔案(如果檔案存在則清空檔案,檔案不存在則建立一個檔案) a或ab 以追加方式開啟檔案,在末尾新增內容,若檔案不存在則建立檔案 r+或rb+ 以可讀、可寫的方式開啟檔案(不建立新檔案) w+或wb+ 以可讀、可寫的方式開啟檔案(如果檔案存在則清空檔案,檔案不存在則建立一個檔案) a+或ab+ 以新增方式開啟檔案,開啟檔案並在末尾更改檔案,若檔案不存在則建立檔案 @author liuguanglei 18601767221@163。com @wechat 18601767221 @website ittimeline。net @version 2020/11/29*/int file_open_close_main(int argc, char* argv[]){ readonly_open_txt(); system(“pause”); return 0;}
因為當前路徑下不存在test。txt檔案,所以程式執行時會出現錯誤提示
以只寫的方式開啟當前目錄下的test。txt文字檔案,如果檔案不存在則會建立檔案,開啟成功返回FILE *p_write
#define _CRT_SECURE_NO_WARNINGS#include #include /* 以只寫的方式開啟當前目錄下的test。txt文字檔案,如果檔案不存在則會建立檔案,開啟成功返回FILE *p_write*/FILE* write_open_txt() { FILE* p_write = fopen(“。/test。txt”, “w”); if (p_write == NULL) { perror(“write mode open file”); return; } else { printf(“open test。txt success!\n”); } return p_write;}/* 檔案的開啟和關閉 fopen()函式開啟方式說明 r或rb 以只讀方式開啟一個文字檔案(不建立檔案,若檔案不存在則報錯) w或wb 以寫方式開啟檔案(如果檔案存在則清空檔案,檔案不存在則建立一個檔案) a或ab 以追加方式開啟檔案,在末尾新增內容,若檔案不存在則建立檔案 r+或rb+ 以可讀、可寫的方式開啟檔案(不建立新檔案) w+或wb+ 以可讀、可寫的方式開啟檔案(如果檔案存在則清空檔案,檔案不存在則建立一個檔案) a+或ab+ 以新增方式開啟檔案,開啟檔案並在末尾更改檔案,若檔案不存在則建立檔案 @author liuguanglei 18601767221@163。com @wechat 18601767221 @website ittimeline。net @version 2020/11/29*/int main(int argc, char* argv[]){ FILE* p_write = write_open_txt(); system(“pause”); return 0;}
程式執行結果
以只寫方式開啟時如果test。txt檔案不存在,系統會建立檔案
檔案的關閉
C語言提供了fclose()函式用於關閉檔案,該函式的宣告也位於stdio。h標頭檔案中,fclose()函式需要的引數就是開啟檔案返回的檔案流指標,通常情況下檔案開啟後如果操作完成應該就要關閉。如果檔案只打開而不關閉,會給程式造成異常。系統設定了一個應用程式只能開啟1024個檔案。
以只寫的方式開啟當前系統路徑下的文字檔案test。txt 後關閉檔案
#define _CRT_SECURE_NO_WARNINGS#include #include /* 以只寫的方式開啟當前目錄下的test。txt文字檔案,如果檔案不存在則會建立檔案,開啟成功返回FILE *p_write*/FILE* write_open_txt() { FILE* p_write = fopen(“。/test。txt”, “w”); if (p_write == NULL) { perror(“write mode open file”); return; } return p_write;}/* 檔案的開啟和關閉 fopen()函式開啟方式說明 r或rb 以只讀方式開啟一個文字檔案(不建立檔案,若檔案不存在則報錯) w或wb 以寫方式開啟檔案(如果檔案存在則清空檔案,檔案不存在則建立一個檔案) a或ab 以追加方式開啟檔案,在末尾新增內容,若檔案不存在則建立檔案 r+或rb+ 以可讀、可寫的方式開啟檔案(不建立新檔案) w+或wb+ 以可讀、可寫的方式開啟檔案(如果檔案存在則清空檔案,檔案不存在則建立一個檔案) a+或ab+ 以新增方式開啟檔案,開啟檔案並在末尾更改檔案,若檔案不存在則建立檔案 @author liuguanglei 18601767221@163。com @wechat 18601767221 @website ittimeline。net @version 2020/11/29*/int main(int argc, char* argv[]){ FILE *p_write=write_open_txt(); //關閉檔案 fclose(p_write); system(“pause”); return 0;}
檔案的順序讀寫
基於字元寫檔案
C語言提供了fputc()函式實現寫入單個字元到檔案中,該函式需要兩個引數:字元和檔案流指標。寫入成功會返回寫入的字元,如果寫入失敗則返回-1
#define _CRT_SECURE_NO_WARNINGS#include #include /* 以字元的方式寫入檔案*/void write_file_char() { //以寫的方式開啟當前目錄下的test。txt檔案 FILE* p_write = fopen(“。/test。txt”,“w”); if (NULL==p_write) { perror(“write mode open fail”); return; } //將字元a寫入當前目錄下的test。txt檔案 char c=fputc(‘a’, p_write); printf(“寫入test。txt文字檔案的字元內容是%c\n”,c);}/* 基於字元的檔案寫操作 @author liuguanglei 18601767221@163。com @wechat 18601767221 @website ittimeline。net @version 2020/11/29*/int main(int argc, char* argv[]){ write_file_char(); system(“pause”); return 0;}
程式執行結果
檢視當前目錄D:\workspace\c\ittimelinedotnet\vs2019\c-core\c-core-advanced 下test。txt檔案的內容
需要注意的是fputc()函式在寫入字元前,預設會清空檔案的內容,如果要是追加檔案,將開啟檔案的開啟方式改成追加即可。
//以追加的方式開啟當前目錄下的test。txt檔案 FILE* p_append = fopen(“。/test。txt”,“a”);
而如果想要往檔案追加寫入字串,藉助迴圈就可以搞定
#define _CRT_SECURE_NO_WARNINGS#include #include /* 以追加的方式將字元寫入test。txt文字檔案中*/void append_file_str() { FILE* p_append = fopen(“。/test。txt”, “a”); if (NULL == p_append) { perror(“write mode open fail”); return; } //以追加的方式將字串hello world寫入test。txt文字檔案中 char buf[] = “hello world”; int i = 0; while (buf[i] != 0) { fputc(buf[i], p_append); i++; }}/* 基於字元的檔案寫操作 @author liuguanglei 18601767221@163。com @wechat 18601767221 @website ittimeline。net @version 2020/11/29*/int main(int argc, char* argv[]){ append_file_str(); system(“pause”); return 0;}
寫入字串
基於字元讀檔案
C語言提供了fgetc()函式實現基於字元從字元流指標中讀取檔案,該函式的引數只需要傳遞檔案流指標即可,讀取成功會返回讀取的字元,讀取失敗返回-1。
#define _CRT_SECURE_NO_WARNINGS#include #include char buf[128] = “”;/* 讀取指定路徑的檔案並返回char**/char * read_text_by_char(char *path) { //以只讀的方式開啟當前路徑下的test。txt檔案 FILE *p_readonly=fopen(path,“r”); if (NULL==p_readonly) { perror(“read file error”); return NULL; } int i= 0; //EOF 表示-1 即當沒有讀取失敗時將讀取的值賦值給buf[i ] //fgetc()的返回值是-1表示失敗,當讀取的檔案中有-1時,就會提前結束,因此不能使用EOF while ((buf[i++] = fgetc(p_readonly)) != EOF); return &buf;}/* 基於字元讀取檔案 @author liuguanglei 18601767221@163。com @wechat 18601767221 @website ittimeline。net @version 2020/11/29*/int main(int argc, char* argv[]){ char *buffer=read_text_by_char(“。/test。txt”); printf(“test。txt content is %s\n”, buffer); system(“pause”); return 0;}
程式執行結果
fgetc()函式在基於字元讀取檔案時,如果檔案內容有-1這種數字,就不能使用EOF作為檔案的結尾。
為了解決這個問題,C語言提供了feof()函式用於檢測是否讀到了檔案的結尾。即判斷最後一次讀操作的內容不是當前位置的內容(上一個內容),該函式的返回值如果是0就表示沒有到檔案結尾,如果是非0就表示到檔案結尾。
#define _CRT_SECURE_NO_WARNINGS#include #include char buf[128] = “”;/* 使用feof()函式判斷檔案是否讀取完畢,解決了fgetc()函式讀取-1字元的二進位制返回-1時讀取就停止的問題*/char* read_text(char * path) { //以只讀的方式開啟當前路徑下的test。txt檔案 FILE* p_readonly = fopen(path, “r”); if (NULL == p_readonly) { perror(“read file error”); return NULL; } int i = 0; int pos = 0; do { buf[i] = fgetc(p_readonly); i++; pos = feof(p_readonly); // pos ==0 表示沒有到檔案末尾 } while (pos==0); return buf;}/* 基於字元讀取檔案 @author liuguanglei 18601767221@163。com @wechat 18601767221 @website ittimeline。net @version 2020/11/29*/int main(int argc, char* argv[]){ char *buffer=read_text(“。/test。txt”); printf(“test。txt content is %s\n”, buffer); system(“pause”); return 0;}
基於字元檔案的複製
檔案的複製就是將一個檔案建立一個副本。
而文字檔案的複製的實現過程就是首先讀取一個檔案,然後將讀取的檔案內容寫入到待複製的檔案中。
我這裡封裝了一個函式
copy_text_file(char* source_path,char *target_path)
,只需要傳遞原始檔和目標檔案就可以實現檔案的複製
#define _CRT_SECURE_NO_WARNINGS#include #include /* 封裝一個文字檔案的複製方法,傳遞兩個檔案的路徑實現將source_path的檔案內容複製到target_path*/void copy_text_file(char* source_path,char *target_path) { FILE* source = fopen(source_path,“r”); if (NULL == source) { perror(“open file error”); } FILE* target = fopen(target_path,“w”); if (NULL == target) { perror(“open file target。txt error”); } char ch = 0; while (1) { //讀取檔案的內容 ch=fgetc(source); //讀取到函式的末尾 if (feof(source)) { break; } //將讀取的檔案內容寫入到target檔案指標 fputc(ch, target); } //關閉檔案 fclose(target); fclose(source);}/* 實現檔案複製 @author liuguanglei 18601767221@163。com @wechat 18601767221 @website ittimeline。net @version 2020/11/29*/int main(int argc, char* argv[]){ copy_text_file(“。/test。txt”,“。/target。txt”); system(“pause”); return 0;}
程式執行結果
檢視複製的原始檔和目標檔案的內容
但是
copy_text_file(char* source_path,char *target_path)
函式只能實現文字檔案的複製,如果想要實現二進位制檔案(例如圖片)的複製,需要稍微改造下fopen()方法
#define _CRT_SECURE_NO_WARNINGS#include #include /* 二進位制檔案的複製*/void copy_binary_file(char* source_path, char* target_path) { FILE* source = fopen(source_path, “rb”); if (NULL == source) { perror(“open file error”); } FILE* target = fopen(target_path, “wb”); if (NULL == target) { perror(“open file target。txt error”); } char ch = 0; while (1) { //讀取檔案的內容 ch = fgetc(source); //讀取到函式的末尾 if (feof(source)) { break; } //將讀取的檔案內容寫入到target檔案指標 fputc(ch, target); } //關閉檔案 fclose(target); fclose(source);}/* 實現檔案複製 @author liuguanglei 18601767221@163。com @wechat 18601767221 @website ittimeline。net @version 2020/11/29*/int main(int argc, char* argv[]){ copy_binary_file(“。/20201129。jpg”,“。/20201129_copy。jpg”); system(“pause”); return 0;}
程式執行結果
基於字元的檔案檢視
將指定的檔案內容寫入到終端
#define _CRT_SECURE_NO_WARNINGS#include #include void cat_file(char* file_path) { //以只讀方式開啟當前目錄的text_file_cat。c檔案 FILE *p=fopen(file_path,“r”); if (NULL==p) { perror(“file read error”); return; } char ch = 0; while (1) { //讀取指定的內容 ch = fgetc(p); //如果讀到檔案末尾 if (feof(p)) { break; } //將讀取的檔案內容寫入到終端 //stdout表示終端的檔案流指標 //printf()底層會呼叫fputc() fputc(ch,stdout); } //關閉檔案 fclose(p);}/* 文字檔案檢視 @author liuguanglei 18601767221@163。com @wechat 18601767221 @website ittimeline。net @version 2020/11/29*/int main(int argc, char* argv[]){ cat_file(“text_file_cat。c”); return 0;}
程式執行結果
基於行寫檔案
C語言提供了
fputs(const char*str,FILE *stream)
函式用於將字串寫入到指定的檔案,其中str是待寫入檔案的字串地址,stream表示檔案流指標,字串結束符 ‘\0’ 停止寫入,寫入成功返回0,寫入失敗返回-1。
#define _CRT_SECURE_NO_WARNINGS#include #include /* fputs()寫入字串到指定的檔案 @author liuguanglei 18601767221@163。com @wechat 18601767221 @website ittimeline。net @version 2020/11/29*/int main(int argc, char* argv[]){ //以只寫的方式開啟當前路徑下的test。txt FILE* p = fopen(“test。txt”,“w”); if (NULL==p) { perror(“open file fail”); } char buf[] = “I will see you again\0again ”; //將字串I will see you again 寫入到test。txt檔案中 //遇到\0停止寫入 int result = fputs(buf,p); if (result==0) { printf(“寫入資料成功\n”); } else { printf(“寫入資料失敗”); } system(“pause”); return 0;}
程式執行結果
基於行讀檔案
C語言提供了
fgets(char* str, int size ,FILE *stream)
函式用於從檔案讀取字串,其中str用於儲存讀取的內容,而size是指定最大讀取字串的長度(size-1),stream表示檔案流指標。fgets()函式遇到
\n
會結束。
讀取成功則返回讀取的字串,讀取到檔案末尾或則失敗則返回NULL。需要注意的是 fgets()函式只能操作字串,不能操作二進位制檔案。
#define _CRT_SECURE_NO_WARNINGS#include #include /* 基於fgets()讀取指定檔案的字串 @author liuguanglei 18601767221@163。com @wechat 18601767221 @website ittimeline。net @version 2020/11/29*/int main(int argc, char* argv[]){ FILE* p = fopen(“。/test。txt”, “r”); if (NULL == p) { perror(“讀取檔案錯誤”); return; } char buf[1024] = “”; fgets(buf, sizeof(buf), p); printf(“buf = %s \n”, buf); system(“pause”); return 0;}
程式執行結果
基於fputs()函式和fgets()函式實現的文字檔案複製
#define _CRT_SECURE_NO_WARNINGS#include #include #include /* 基於fputs()和fgets()函式實現的檔案複製 只能支援文字檔案,因為遇到\0會停止讀寫*/void copy_by_fgets_fputs(char * source_path,char *target_path) { FILE* source = fopen(source_path,“r”); if (NULL==source) { perror(“open file fail”); return; } FILE* target = fopen(target_path,“w”); if (NULL == target) { perror(“open file fail”); return; } char* p = NULL; //迴圈多少次,buf就可以使用多少次 char buf[1024] = “”; while (1) { memset(buf, 0, sizeof(buf)); p = fgets(buf, sizeof(buf), source); //讀取結束 if (p==NULL) { break; } //將讀取的內容寫入到target fputs(buf, target); } // fclose(target); fclose(source);}/* 基於fputs()和fgets()函式實現的檔案內容複製 @author liuguanglei 18601767221@163。com @wechat 18601767221 @website ittimeline。net @version 2020/11/29*/int main(int argc, char* argv[]){ copy_by_fgets_fputs(“。/test。txt”,“。/copy_of。txt”); system(“pause”); return 0;}
程式執行結果
基於檔案讀寫實現隨機生成表示式並完成四則運算
首先定義一個宏,用於描述生成表示式的數量
//表示式生成的數量#define EXPRESSION_NUM 10
由於生產表示式,需要讀寫檔案,在讀寫檔案之前,需要開啟檔案,因此封裝一個開啟檔案的函式,該函式需要一個引數:開啟方式
而FILE_PATH 也是宏定義的檔案路徑
#define FILE_PATH “expression_calc。txt”
,算術運算表示式就是儲存在該檔案中。
/* 按照指定的開啟方式開啟指定路徑的檔案*/FILE* open_file(char * mode) { FILE* fp=fopen(FILE_PATH, mode); if (NULL==fp) { perror(“open file fail”); return; } return fp;}
然後實現生成表示式的函式。以及將生成的表示式寫入檔案中。
這其中表達式的運算元和運算子都是是隨機生成的,需要藉助srand()函式和rand()函式。
然後還要使用sprintf()函式組包生成表示式。
/* 生成表示式並寫入檔案*/void generator_expression_write_file() { FILE *fp=open_file(“w”); //設定隨機種子 srand(time(NULL)); int left = 0; int right = 0; char opertors[4] = {‘+’,‘-’,‘*’,‘/’}; char operator_index=0; char singleton_expression[128] = “”; //生成10個隨機表示式字串 for (int i = 0; i < EXPRESSION_NUM;i++) { //產生1-100之間的隨機數 left= rand()%100+1; right= rand()%100+1; //產生0-3之間的隨機數賦值給operator_index operator_index =rand() % 4; //將 “%d %c %d = \n”,left, opertors[operator_index],right 賦值給expression sprintf(singleton_expression,“%d%c%d=\n”,left, opertors[operator_index],right); fputs(singleton_expression, fp); } fclose(fp);}
然後實現讀取檔案後計算表示式,並將表示式的結果寫入FILE_PATH中。
/* 計算表示式的結果並寫入檔案*/void calc_expression_write_file() { FILE* fp = open_file(“r”); //第一個運算元 int left = 0; //第二個運算元 int right = 0; //運算子 char operator=0; //運算的結果 int result = 0; //讀取的單個表示式(未計算) char buf[128] = “”; //單個表示式(已經計算結果的) char singleton_expression[128] = “”; //所有的表示式 char all_expression[100][128] = { 0 }; //讀取一行表示式的指標 char* p = NULL; int i = 0; printf(“生成的表示式列表\n”); while (1) { //每次讀取一行 p = fgets(buf, sizeof(buf), fp); if (p == NULL) { break; } //將讀取的內容buf 解析到left, operator,right變數中 sscanf(buf, “%d%c%d”, &left, &operator,&right); //根據運算的符來進行運算並儲存運算的結果 switch (operator) { case ‘+’: result = left + right; break; case ‘-’: result = left - right; break; case ‘*’: result = left * right; break; case ‘/’: result = left + right; break; } //2+3=5 //將表示式以及計算的結果寫入all_expression二維陣列中 sprintf(all_expression[i], “%d%c%d=%d\n”, left, operator,right, result); printf(“%s \n”, all_expression[i]); i++; } //關閉檔案 fclose(fp); // FILE *n_fp = open_file(“w”); for (int j = 0; j < i; j++) { int result =fputs(all_expression[j], n_fp); if (result==0) { printf(“寫入%s到檔案%s成功\n”, all_expression[j],FILE_PATH); } else { printf(“寫入%s到檔案%s失敗\n”, all_expression[j], FILE_PATH); } } fclose(n_fp);}
最後在main函式中依次呼叫這兩個函式並執行即可
#define _CRT_SECURE_NO_WARNINGS#include#include#include#include
程式執行結果
程式執行結果
基於指定格式寫檔案
C語言提供了
fprintf(FILE * stream, const char * format, 。。。)
函式用於將資料按照指定的格式寫入到指定的檔案中,其中stream表示檔案流指標,而format就是資料的格式,。。。表示需要組裝的資料列表。
fprintf()函式寫入成功則會返回實際寫入的字元個數,如果寫入失敗則返回-1。
#define _CRT_SECURE_NO_WARNINGS#include #include /* fprintf()函式按照指定的格式寫入資料到文字檔案 @author liuguanglei 18601767221@163。com @wechat 18601767221 @website ittimeline。net @version 2020/11/29*/int main(int argc, char* argv[]){ char name[] = “劉光磊”; double height = 178。00; int age = 28; FILE* fp = fopen(“。/info。txt”,“w”); if (NULL==fp) { perror(“read file fail”); return -1; } fprintf(fp, “我的名字是%s我的身高是%。2lf我的年齡是%d\n”, name, height, age); fclose(fp); system(“pause”); return 0;}
程式執行結果
程式執行結果
基於指定格式讀檔案
C語言提供了
fscanf(FILE * stream, const char * format, 。。。);
函式用於從檔案中按照指定的格式來讀取資料,讀取成功返回成功轉換引數的個數,失敗返回-1。
#define _CRT_SECURE_NO_WARNINGS#include #include /* 使用fscanf()從檔案中讀取指定格式的資料 @author liuguanglei 18601767221@163。com @wechat 18601767221 @website ittimeline。net @version 2020/11/29*/int main(int argc, char* argv[]){ int year = 0; int month = 0; int day = 0; FILE* fp = fopen(“。/date。txt”, “r”); if (NULL == fp) { perror(“read file fail”); return -1; } //從date。txt檔案中讀取日期並賦值給year,month,day fscanf(fp, “%d-%d-%d”, &year, &month, &day); printf(“year = %d\n”,year); printf(“month = %d\n”, month); printf(“day = %d\n”, day); system(“pause”); return 0;}
讀取的檔案內容
程式執行結果
基於塊寫檔案
在讀寫大檔案時需要基於塊來讀寫檔案,C語言提供了
fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
函式來基於塊寫檔案,其中ptr表示寫入檔案資料的地址,size表示寫入檔案內容的大小,nmemb表示寫入檔案的塊數,寫入檔案的總大小為szie * nmemb,stream表示已經開啟的檔案流指標。寫入成功則返實際成功寫入檔案資料的塊數目,該值和nmemb相等。
將結構體陣列user_array以塊的形式寫入fwrite。txt檔案
#define _CRT_SECURE_NO_WARNINGS#include #include //定義一個結構體user,別名是userstypedef struct user { int id; char name[16];}users;/* fwrite()函式:基於塊寫檔案 @author liuguanglei 18601767221@163。com @wechat 18601767221 @website ittimeline。net @version 2020/11/29*/int main(int argc, char* argv[]){ //初始化一個結構體陣列 users user_array[3] = { {1,“tony”},{2,“mengmeng”},{3,“xiaohuihui”} }; FILE* fp = fopen(“fwrite。txt”,“w”); if (NULL==fp) { perror(“open file fail”); return -1; } //fwrite()第二個引數寫1,返回值剛好是寫入檔案的位元組數 int byte_size=fwrite(user_array, 1, sizeof(user_array), fp); printf(“寫入檔案的位元組數是%d\n”,byte_size); system(“pause”); return 0;}
基於塊讀檔案
C語言提供了
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
函式用於基於塊的方式讀取檔案內容
其中ptr:存放讀取出來資料的記憶體空間,size: size_t 為 unsigned int型別,此引數指定讀取檔案內容的塊資料大小,nmemb:讀取檔案的塊數,讀取檔案資料總大小為:size * nmemb,stream:已經開啟的檔案指標
如果讀取成功則返回實際成功讀取到內容的塊數,如果此值比nmemb小,但大於0,說明讀到檔案的結尾。否則失敗返回0
實現讀取fwrite。txt檔案的內容,由於該檔案的內容是一個結構體陣列,因此需要在標頭檔案user。h中定義一個通用的結構體,其他的原始檔使用該結構體時包含
#include “user。h”
即可
#pragma once//定義一個結構體user,別名是userstypedef struct user { int id; char name[16];}users;
然後使用fread()函式讀取檔案內容並存儲到結構體陣列中,然後遍歷結構體陣列的內容,顯示在終端上。
#define _CRT_SECURE_NO_WARNINGS#include #include #include “user。h”#include /* fread()基於塊的方式讀取檔案 @author liuguanglei 18601767221@163。com @wechat 18601767221 @website ittimeline。net @version 2020/11/29*/int main(int argc, char* argv[]){ users user_array[3]; //將陣列清零 memset(user_array, 0, sizeof(user_array)); int size = sizeof(user_array) / sizeof(user_array[0]); FILE* fp = fopen(“。/fwrite。txt”,“r”); //一次性讀到user_array fread(&user_array, 1, sizeof(user_array), fp); for (int i = 0; i < size;i++) { //從fp讀取一個結構體users並賦值給&user_array[i] //fread(&user_array[i], 1, sizeof(users), fp); printf(“id = %d name = %s \n”,user_array[i]。id,user_array[i]。name); } system(“pause”); return 0;}
程式執行效果
檔案的隨機讀寫
之前介紹的讀寫檔案函式都是順序讀寫的。
C語言提供了
fseek(FILE *stream, long offset, int whence)
函式實現移動檔案流(檔案游標)的讀寫位置實現隨機讀寫。
fseek(fp,6,SEEK_SET)表示游標相對於開頭向後移動6個位元組
fseek(fp,0,SEEK_SET)表示游標移動至開頭
fseek(fp,4,SEEK_CUR)表示游標相對當後移動4個位元組
fseek(fp,-4,SEEK_CUR)表示游標相對當前移動4個位元組
fseek(fp,4,SEEK_END)表示游標相對末尾往後移動4個位元組
除此以外C語言還提供了rewind(fp)函式將游標移到開頭,等價於fseek(fp,0,SEEK_SET),ftell(fp)函式可以獲取檔案流(檔案游標)的讀寫位置。
#define _CRT_SECURE_NO_WARNINGS#include #include #include /* fseek()隨機讀寫 @author liuguanglei 18601767221@163。com @wechat 18601767221 @website ittimeline。net @version 2020/11/29*/int main(int argc, char* argv[]){ FILE* fp = fopen(“fseek。txt”, “w”); if (NULL == fp) { perror(“open file fail”); return 0; } //往fseek。txt檔案寫入this is test content 此時游標預設在末尾 fputs(“this is test content”, fp); //移動游標到首部 fseek(fp, 0, SEEK_SET); //寫入seek到fseek。txt,此時檔案內容是seek is test content fputs(“seek”, fp); //移動游標到倒數第五個位元組 fseek(fp,-5,SEEK_END); //在倒數第五個位元組的游標寫入again,此時檔案內容是seek is test coagain fputs(“again”, fp); //將游標移到開頭 //rewind(fp); //將游標移到末尾 fseek(fp, 0, SEEK_END); //計算末尾到開頭的位元組數 int byte_size=ftell(fp); printf(“byte_size=%d\n”,byte_size); int count = ftell(fp); system(“pause”); return 0;}
程式執行結果
獲取檔案資訊
C語言提供了
stat(const char *path, struct stat *buf)
函式用於獲取檔案資訊,如果獲取成功返回0,獲取失敗則返回-1。stat()函式可以判斷該檔案存不存在獲取檔案狀態。
在使用該函式前需要包含兩個系統路徑下的標頭檔案:sys/types。h和sys/stat。h
#define _CRT_SECURE_NO_WARNINGS#include #include #include #include /* stat()函式獲取檔案狀態 @author liuguanglei 18601767221@163。com @wechat 18601767221 @website ittimeline。net @version 2020/11/29*/int main(int argc, char* argv[]){ //定義stat結構體 struct stat file_info; int info= stat(“fwrite。txt”,&file_info); //檔案不存在 if (info<0) { //列印輸出提示資訊 printf(“file not found \n”); } else { printf(“獲取檔案資訊: 檔案大小是%d位元組 \n”, file_info。st_size); } system(“pause”); return 0;}
程式執行結果
檔案刪除和重新命名
檔案刪除
C語言提供了庫函式
remove(const char *path)
來刪除一個檔案,引數需要一個檔案的路徑,刪除成功返回0,刪除失敗返回-1
#define _CRT_SECURE_NO_WARNINGS#include #include /* 檔案刪除 @author liuguanglei 18601767221@163。com @wechat 18601767221 @website ittimeline。net @version 2020/11/29*/int main(int argc, char* argv[]){ //刪除當前路徑下的fwrite。txt檔案 int flag=remove(“fwrite。txt”); if (flag==0) { printf(“刪除成功\n”); } else { printf(“刪除失敗”); } system(“pause”); return 0;}
程式執行結果
檔案重新命名
C語言提供了
rename(const char *oldpath, const char *newpath)
函式實現檔案的重新命名,該函式需要兩個檔案的完整路徑
#define _CRT_SECURE_NO_WARNINGS#include #include /* rename()函式實現檔案的重新命名 @author liuguanglei 18601767221@163。com @wechat 18601767221 @website ittimeline。net @version 2020/11/29*/int main(int argc, char* argv[]){ //檔案重新命名 rename(“fseek。txt”,“fseek_rename。txt”); system(“pause”); return 0;}
程式執行結果
Linux和Windows文字檔案的區別
Windows字串中帶
\n
時存取的是
\r\n
Linux中字串帶
\n
時存取的是
\n
如果把Windows的文字檔案傳到Linux系統上時使用編輯器開啟會多了一個
\r
。而Linux的文字檔案到Windows下就沒有換行。
檔案緩衝區
緩衝區本質就是記憶體中的一塊臨時空間,
當寫入檔案時,庫函式會去呼叫核心提供的系統呼叫,例如fwrite()函式最終會呼叫write()系統呼叫。如果是大量頻繁的寫操作,會從使用者空間切換到核心空間,上下文的切換會導致程式效率非常低。
因此引入了檔案的緩衝區,當緩衝區滿了才會一次性寫入檔案,或者有特殊場景可以呼叫
fflush()
強制重新整理緩衝區,也就是將緩衝區的內容寫入檔案,或者程式正常退出(關閉Visual Studio的終端)時也會將緩衝區的檔案寫入磁碟檔案。
#define _CRT_SECURE_NO_WARNINGS#include #include /* 檔案緩衝區 @author liuguanglei 18601767221@163。com @wechat 18601767221 @website ittimeline。net @version 2020/11/29*/int main(int argc, char* argv[]){ FILE* fp = fopen(“data。txt”,“w”); if (NULL==fp) { perror(“open file fail”); return -1; } //當不關閉終端時data。txt檔案沒有內容 fputs(“this is test content ”,fp); system(“pause”); return 0;}
緩衝區
重新整理緩衝區的兩種編碼實現
#define _CRT_SECURE_NO_WARNINGS#include #include #include /* 檔案緩衝區 @author liuguanglei 18601767221@163。com @wechat 18601767221 @website ittimeline。net @version 2020/11/29*/int main(int argc, char* argv[]){ FILE* fp = fopen(“data。txt”,“w”); if (NULL==fp) { perror(“open file fail”); return -1; } //當不關閉終端時data。txt檔案沒有內容 fputs(“this is test content ”,fp); //可以呼叫fflush()函式實現強制重新整理緩衝區的內容到data。txt; fflush(fp); //也可以將緩衝區寫滿 char buf[1024] = “”; //寫入1M的1 for (int i = 0; i < 1024;i++) { memset(buf, ‘1’, sizeof(buf)); fwrite(buf, 1, sizeof(buf), fp); } system(“pause”); return 0;}
而Windows下的stdout 是沒有緩衝區的,即使用
printf()
函式列印資料到終端時不會進入緩衝區,直接寫入到終端上。而Linux下的stdout檔案是有緩衝區的。
Windows下標準輸入(stdin) 不能呼叫fflush()強制重新整理。