C語言實現(xiàn)文件操作實例(簡單圖示講解)
前言
說到文件操作,大家會第一印象想到不就是電腦硬盤中創(chuàng)建文件,寫入數(shù)據(jù)嗎,鍵盤、鼠標就可以搞定,那么接下來我要告訴你的是C語言也可以實現(xiàn)文件操作哦?。。?/p>
首先、我們要實現(xiàn)的文件操作,不只是簡單的打開和關(guān)閉,寫入數(shù)據(jù),我們也可以從相對應(yīng)文件中,讀取數(shù)據(jù),到程序中,使得程序能順利運行,并對傳來的數(shù)據(jù)進行處理使用!
一、為什么要使用文件操作
我們在前文中,實現(xiàn)了靜態(tài)、動態(tài)通訊錄,但是我們也發(fā)現(xiàn)了,只要程序關(guān)閉,那么添加的通訊錄的信息數(shù)據(jù),也就一起消失,我們下次打開的時候,通訊錄是空的,那么這不太符合我們實際所需。我們需要的是,可以利用、可以存儲、可以保存的通訊錄,那么我們就必須了解一下,文件操作。
文件操作:使用文件操作我們可以將數(shù)據(jù)直接存放到電腦的硬盤上,做到數(shù)據(jù)的持久化
二、文件
磁盤上的文件就是文件。
在程序設(shè)計中,我們一般說的文件有兩種,分別是程序文件、數(shù)據(jù)文件(從文件功能的角度進行分類)。
2.1 程序文件
包括源程序文件(后綴為.c),目標文件(windows環(huán)境后綴為.obj),可執(zhí)行程序(windows環(huán)境后綴為.exe)。
2.2數(shù)據(jù)文件
文件的內(nèi)容不一定是程序,而是程序運行時讀寫的數(shù)據(jù),比如程序運行需要從中讀取數(shù)據(jù)的文件,或者輸出內(nèi)容的文件。
由上文對于兩種文件的介紹,我們可知,我們需要了解的是數(shù)據(jù)文件,本文就主要講解一下,如何處理數(shù)據(jù)文件,并對該文件進行操作
2.3 文件名
文件名是一個文件的唯一的文件標識,以便于用戶識別和引用。
比如:D:\new.txt 這是D盤下的文件名為new的文本文檔
三、文件操作
我們之前在寫C語言程序的時候,都是通過鍵盤和屏幕,進行輸入和輸出數(shù)據(jù),我們這一次要將數(shù)據(jù)輸入和讀取數(shù)據(jù)都是于硬盤有關(guān)。
3.1 文件打開和關(guān)閉
我們首先要了解的是我們之前一直所處理的,輸入輸出數(shù)據(jù)都是以終端為對象的,即從終端的鍵盤輸入數(shù)據(jù),運行結(jié)果顯示到顯示器上。
如圖:
文件操作需要的是,以硬盤中的文件為數(shù)據(jù)輸入輸出對象。
如圖:
1. 文件指針
在緩沖文件系統(tǒng)中,關(guān)鍵的概念是 “ 文件類型指針 ”,即文件指針
文件信息區(qū):
每個被使用的文件都在內(nèi)存中開辟了一個相應(yīng)的文件信息區(qū),用來存放文件的相關(guān)信息(如文件的名字,文件狀態(tài)及文件當前的位置等)。這些信息是保存在一個結(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;//就是一個結(jié)構(gòu)體類型的FILE,規(guī)定的文件的 所有信息,都是可以存儲在上面
不同編譯器不同的內(nèi)容,但是都是可以存放各種文件類型的結(jié)構(gòu)體FILE,正常使用即可,不用深究
每次打開一個文件,系統(tǒng)就會根據(jù)文件的情況自動創(chuàng)建一個FILE結(jié)構(gòu)的變量,并填充信息,一般都是通過一個FILE指針來維護這個FILE結(jié)構(gòu)的變量,方便使用
如圖:
定義pf是一個指向FILE類型數(shù)據(jù)的指針變量??梢允筽f指向某個文件的文件信息區(qū)(是一個結(jié)構(gòu)體變量)。通過該文件信息區(qū)中的信息就能夠訪問該文件。也就是說,通過文件指針變量能夠找到與它關(guān)聯(lián)的文件。
2. 文件的打開和關(guān)閉
文件在使用前應(yīng)該先打開,之后再關(guān)閉,我們先認識一下C語言給我們提供的文件開關(guān)函數(shù)
第一個fopen來打開文件,第二個fclose來關(guān)閉文件
代碼如下:
//打開文件 FILE * fopen ( const char * filename, const char * mode );//返回文件類型 //關(guān)閉文件 int fclose ( FILE * stream );//返回整型
打開方式:
1.r表示讀 w表示寫 a表示追加(即不會覆蓋原有數(shù)據(jù))
2.帶b,標識的是打開二進制文件
3. +號表示讀寫,如果是r+的話,且沒有指定文件,那么會報錯,但是w+、a+若沒有指定文件,會創(chuàng)建一個指定文件,再輸入數(shù)據(jù)(寫文件)
代碼演示:
3.2 文件的順序讀寫
什么叫做順序讀寫呢,意思就是,在讀取/寫文件的時候,讀取一個或者寫一個內(nèi)容進去,光標就會向后移動一位,有順序的讀取和寫入
如圖:
通過fgetc和fputc函數(shù)解釋了,文件確實是按照順序讀寫的
3.3 函數(shù)使用
輸入流和輸出流介紹:
流的概念:
上面函數(shù)使用于所有輸入流或文件,這是什么意思?
圖示解釋如下:
1. fgetc和fputc
上文圖示用到這兩個函數(shù)
代碼如下:
//這是fgetc函數(shù),可以理解為得到文件中的一個字符 int main() { FILE* pf = fopen("test.txt", "r"); for (int i = 0; i < 10; i++) { char src = fgetc(pf); printf("%c", src); } fclose(pf); pf = NULL; return 0; } //這是fputc函數(shù),可以理解為放置文件中一個字符 int main() { FILE* pf = fopen("test.txt", "w"); for (int i = 0; i < 26; i++) { fputc('a' + i, pf); } fclose(pf); pf = NULL; return 0; }
實際上,fgetc和fputc函數(shù)就是得到或放置一個字符在文件中,返回值為int
2. fgets和fputs
fgets和fputs,可以理解為得到或者放置一個字符串在文件中。
還有一個特點,如圖所示:
fgets如果沒有讀取到內(nèi)容,返回NULL
代碼如下:
//fgets函數(shù) int main() { FILE* pf = fopen("test.txt", "r"); char arr[20]; while (fgets(arr, 20, pf) != NULL) { printf("%s", arr); } fclose(pf); pf = NULL; return 0; } //fputs函數(shù) int main() { FILE* pf = fopen("test.txt", "w"); char arr[20]; fputs("hello world!!\n", pf); fputs("hello why\n", pf); fputs("hello fzx\n", pf); fclose(pf); pf = NULL; return 0; }
3. fscanf和fprintf
實際上和scanf、printf差不多的形式,只是多一個參數(shù) FILE*stream
如圖:
如果fscanf讀取的時候沒有數(shù)據(jù),那么也不會報錯,就認為是沒有讀取吧
代碼如圖所示:
struct S { char name[20]; int age; double score; }; //fscanf格式化讀取pf流中的數(shù)據(jù),賦值給后面的參數(shù),和scanf用法差不多, //只是多一個FILE*類型,后面于scanf用法一致,改用&就用 int main() { FILE* pf = fopen("test.txt", "r+"); //讀寫都行 //格式化輸出 //struct S s = { "why",19,100 }; struct S s = {0}; struct S s1 = { 0 }; fscanf(pf, "%s %d %lf\n", s.name, &(s.age), &(s.score)); printf("%s %d %lf\n", s.name, (s.age), (s.score)); //fscanf(pf, "%s %d %lf\n", s1.name, &(s1.age), &(s1.score)); //printf("%s %d %lf\n", s1.name, (s1.age), (s1.score)); int a = 10; /*fscanf(pf, "%d", a); printf("%d\n", a);*/ //這是進行測試如果沒有相應(yīng)的數(shù)據(jù)讀取的時候,不會報錯,只是相當于沒有這一行代碼,不會發(fā)生任何改變(對相應(yīng)a) fclose(pf); pf = NULL; return 0; } //這是fprintf函數(shù)的使用 int main() { FILE* pf = fopen("test.txt", "r+"); //讀寫都行 //格式化輸出 struct S s = { "why",19,100 }; fprintf(pf, "%s %d %lf\n", s.name, (s.age), (s.score)); //printf("%s %d %lf\n", s.name, (s.age), (s.score)); fclose(pf); pf = NULL; return 0; }
1.fscanf 格式化讀取pf流中的數(shù)據(jù),賦值給后面的參數(shù),和 scanf 用法差不多,只是多一個FILE*類型,后面于 scanf 用法一致,該用&就用
2.fprintf 格式化寫入文件中數(shù)據(jù),上面代碼中,將結(jié)構(gòu)體變量s,數(shù)據(jù),寫入pf管理的文件中
3.fscanf 如果沒有相應(yīng)的數(shù)據(jù)讀取的時候,不會報錯,只是相當于沒有這一行代碼,不會發(fā)生任何改變(對相應(yīng)a)
4. fread和fwrite
只能接收文件流,二進制文件,可能會有亂碼產(chǎn)生
如圖分析:
代碼演示:
struct S { char name[20]; int age; double score; }; //先寫入二進制文件 fwrite int main() { struct S s = { "fzx",18,100 }; FILE* pf = fopen("test.txt", "wb"); //寫一個二進制文件 fwrite(&s, sizeof(struct S), 1, pf); fclose(pf); pf = NULL; return 0; } //再讀取二進制文件 fread int main() { struct S s = { 0 }; FILE* pf = fopen("test.txt", "rb"); //讀取文件 fread(&s, sizeof(struct S), 1, pf); printf("%s %d %lf\n", s.name, s.age, s.score); return 0; }
5. 對比一組函數(shù)
scanf / fscanf / sscanf
printf / fprintf /sprintf
前面四種大家都認識了,那么sscanf和sprintf是什么呢?
如圖所示:
3.4 文件的隨機讀寫
1. fseek
根據(jù)文件指針的位置和偏移量來定位文件指針。(改變光標位置)
代碼演示:
int main() { FILE* pFile; pFile = fopen("example.txt", "wb"); fputs("This is an apple.", pFile); fseek(pFile, 9, SEEK_SET); fputs(" sam", pFile); fclose(pFile); return 0; }
2.ftell
返回文件指針相對于起始位置的偏移量
代碼演示:
int main() { FILE* pFile; pFile = fopen("example.txt", "wb"); int num = ftell(pFile); printf("%d ", num); return 0; }
3.rewind
讓文件指針的位置回到文件的起始位置
圖文演示:
代碼演示:
int main() { FILE* pf = fopen("test.txt", "w"); for (int i = 0; i < 10; i++) { fputc('a' + i, pf); } int num = ftell(pf); printf("%d \n", num); rewind(pf); printf("%d \n", ftell(pf)); fclose(pf); pf = NULL; return 0; }
3.5 文本文件和二進制文件
1.根據(jù)數(shù)據(jù)的組織形式,數(shù)據(jù)文件被稱為文本文件或者二進制文件。
2.數(shù)據(jù)在內(nèi)存中以二進制的形式存儲,如果不加轉(zhuǎn)換的輸出到外存,就是二進制文件。
3.如果要求在外存上以ASCII碼的形式存儲,則需要在存儲前轉(zhuǎn)換。以ASCII字符的形式存儲 的文件就是文本文件。
字符一律以ASCII形式存儲,數(shù)值型數(shù)據(jù)既可以用ASCII形式存儲,也可以使用二進制形式存儲。
測試文本文件和二進制文件代碼:
int main() { int a = 10000; FILE* pf = fopen("test.txt", "wb"); fwrite(&a, 4, 1, pf);//二進制的形式寫到文件中 fclose(pf); pf = NULL; return 0; }
3.6文件讀取結(jié)束的判定
在文件讀取過程中,不能用feof函數(shù)的返回值直接用來判斷文件的是否結(jié)束,而是應(yīng)用于當文件讀取結(jié)束的時候,判斷是讀取失敗結(jié)束,還是遇到文件尾結(jié)束。
1.文本文件讀取是否結(jié)束,判斷返回值是否為 EOF ( fgetc ),或者 NULL ( fgets )
fgetc 判斷是否為 EOF .
fgets 判斷返回值是否為 NULL2. 二進制文件的讀取結(jié)束判斷,判斷返回值是否小于實際要讀的個數(shù)。
fread判斷返回值是否小于實際要讀的個數(shù)
圖示:
代碼演示:
//文本文件 int main(void) { int c; // 注意:int,非char,要求處理EOF FILE* fp = fopen("test.txt", "r"); if(!fp) { perror("File opening failed"); return EXIT_FAILURE; } //fgetc 當讀取失敗的時候或者遇到文件結(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); }
總結(jié)
主要講解文件操作的的一些函數(shù),fopen、fclose、fgets、fwrite等等函數(shù)的介紹,以及對于文件的分類、輸入和輸出流的分析,以及更多文件操作的函數(shù)圖示分解。
到此這篇關(guān)于C語言實現(xiàn)文件操作的文章就介紹到這了,更多相關(guān)C語言實現(xiàn)文件操作內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++中使用function和bind綁定類成員函數(shù)的方法詳解
這篇文章主要介紹了C++中使用function和bind綁定類成員函數(shù)的方法,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-11-11C語言中的時間函數(shù)clock()和time()你都了解嗎
這篇文章主要為大家詳細介紹了C語言中的時間函數(shù)clock()和time(),文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助2022-02-02