一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

C語(yǔ)言之文件操作

 山峰云繞 2020-07-11

https://m./i6847739122979701260/?app=news_article&timestamp=1594400967&use_new_style=1&req_id=20200711010927010129040078043AEF9D&group_id=6847739122979701260 



前些時(shí)候,我們學(xué)習(xí)的C語(yǔ)言程序都是由輸入輸出和算法組成的控制臺(tái)程序。我們?cè)诮K端上來(lái)輸入我們提供的數(shù)據(jù),然后程序也會(huì)通過(guò)終端來(lái)告訴我們最終運(yùn)行的結(jié)果。

但是,可能有的同學(xué)已經(jīng)觀(guān)察到了,我們?nèi)粘J褂玫膭e人開(kāi)發(fā)的程序,大多數(shù)都是通過(guò)文件來(lái)提供數(shù)據(jù)的。比如一個(gè)Excel的報(bào)表,程序可以直接來(lái)分析里面的數(shù)據(jù)。再比如,一個(gè)TXT格式的電子書(shū),程序可以直接分析有多少字、多少個(gè)章節(jié),甚至還可以生成出一個(gè)目錄來(lái)。

擁有這樣能力的程序,是不是感覺(jué)功能強(qiáng)大了許多?這就要用到我們今天要講到的內(nèi)容——「文件操作」。

關(guān)于文件

在我們比較熟悉的Windows系統(tǒng)下,文件類(lèi)型的區(qū)分是用「擴(kuò)展名」來(lái)進(jìn)行的。但其實(shí)擴(kuò)展名并不是指「文件格式」,它只是一個(gè)「門(mén)牌號(hào)」而已。至于它到底對(duì)不對(duì),那系統(tǒng)就不知道了??赡苡泻芏嗟男率?,在遇到格式的問(wèn)題的時(shí)候,會(huì)認(rèn)為直接更改擴(kuò)展名,就能實(shí)現(xiàn)格式轉(zhuǎn)換。不瞞你們說(shuō),我小時(shí)候也有過(guò)這種想法。但是后來(lái)發(fā)現(xiàn),不行。舉個(gè)例子,現(xiàn)在有一個(gè) MP3 的文件,要轉(zhuǎn)成 AAC。這兩個(gè)文件從編碼上來(lái)講,就是不一樣的。MP3 只能用 MP3 的方式去讀取,AAC 只能用 AAC 的方式去讀取。如果你把擴(kuò)展名直接改成 AAC,那么系統(tǒng)就被你騙了,就會(huì)用 AAC 的方式去讀取實(shí)際還是 MP3 的文件,當(dāng)然是不行了。

不同的擴(kuò)展名,就對(duì)應(yīng)了不同的讀取方式。「EXE」 就代表 Windows 系統(tǒng)下的可執(zhí)行二進(jìn)制文件,「TXT」是純文本文件,等等。

在 Linux 和 Unix 操作系統(tǒng)下,文件的定義就寬泛多了。不光軟件,硬件也可以叫文件。也就是說(shuō),硬件實(shí)際上也是當(dāng)做文件的方式來(lái)處理的。

在C語(yǔ)言中,文件一般分為兩種,一種是二進(jìn)制文件,就是我們編譯出來(lái)的那個(gè)東西,我們是看不懂的;另一種是文本文件,也就是我們常說(shuō)的源代碼。

打開(kāi)和關(guān)閉文件

我們要對(duì)一個(gè)文件進(jìn)行操作,首先我們需要把文件打開(kāi),然后才能讀或者寫(xiě)。對(duì)文件操作完成后,我們還要將文件關(guān)閉。

C語(yǔ)言中的打開(kāi)文件使用fopen函數(shù),通式如下:

fopen('文件路徑', '模式')

如果打開(kāi)文件成功,則會(huì)返回一個(gè)FILE結(jié)構(gòu)的指針,通過(guò)這個(gè)指針,我們就可以對(duì)這個(gè)文件進(jìn)行操作;如果打開(kāi)文件失敗,則會(huì)返回NULL。

下面是所有的模式:

前面幾個(gè)都好理解,只是最后一個(gè),為啥要區(qū)分一個(gè)二進(jìn)制出來(lái)呢?

不加「b」的情況下,就是以文本的形式來(lái)打開(kāi)。因?yàn)樵诓煌牟僮飨到y(tǒng)中,換行符是不同的。Unix系統(tǒng)用\n,MacOS用\r,而Windows用的是\r\n,那么在文本模式下打開(kāi),C語(yǔ)言會(huì)根據(jù)系統(tǒng)環(huán)境的不同,來(lái)轉(zhuǎn)化換行符。而在二進(jìn)制的模式下,就不會(huì)進(jìn)行任何的轉(zhuǎn)換。

當(dāng)你對(duì)文件操作完畢后,一定要記得把文件用fclose()函數(shù)來(lái)關(guān)閉。其實(shí)我們?cè)诖蜷_(kāi)文件后的所有操作,實(shí)際上都被記錄到了緩存里,只有執(zhí)行了關(guān)閉后,我們的更改才會(huì)生效。如果關(guān)閉成功,則函數(shù)會(huì)返回0;失敗的話(huà),就會(huì)返回EOF。關(guān)閉成功后,我們創(chuàng)建的文件指針就會(huì)失效。

//Example 01//學(xué)習(xí)交流群:782648055#include <stdio.h>#include <stdlib.h>int main(void){ FILE* f; int chr; if ((f = fopen('file1.txt', 'r')) == NULL) { printf('打開(kāi)失?。n'); exit(EXIT_FAILURE); } while ((chr = getc(f)) != EOF) { putchar(chr); } fclose(f); return 0;}
//file1.txt中的內(nèi)容C programming makes me happy!
//Consequence 01C programming makes me happy!

順序讀寫(xiě)文件

打開(kāi)了文件之后,就可以進(jìn)行我們的操作了。

讀寫(xiě)單個(gè)字符

讀取單個(gè)字符,我們可以用fgetc和getc這兩個(gè)來(lái)實(shí)現(xiàn)。它們的作用,就是讀取一個(gè)字符,然后將光標(biāo)移動(dòng)到下一個(gè)位置。

#include <stdio.h>...int fgetc(FILE* stream);int getc(FILE* stream);

函數(shù)的參數(shù),是一個(gè)FILE結(jié)構(gòu)體的指針,也就是一個(gè)準(zhǔn)確讀取的文件流。讀取成功就會(huì)將讀取到的unsigned char內(nèi)容轉(zhuǎn)化為int并返回;文件結(jié)束或者讀取失敗就返回EOF。

這倆函數(shù)不同的地方就在于,fgetc是函數(shù)實(shí)現(xiàn),而getc是用宏實(shí)現(xiàn)。宏會(huì)產(chǎn)生大量的代碼量,但是沒(méi)有函數(shù)調(diào)用堆棧的步驟,所以速度會(huì)快很多。但是宏的展開(kāi)可能會(huì)多次調(diào)用參數(shù),因此如果參數(shù)中含有自增、自減這種副作用的的方法,就只能用函數(shù)實(shí)現(xiàn)的fgetc了。

寫(xiě)入單個(gè)字符,我們可以用fputc和putc,帶有f的,就是函數(shù),另一個(gè)就是宏的實(shí)現(xiàn)的了。

#include <stdio.h>...int fputc(int c, FILE* stream);int putc(int c, FILE* stream);

第一個(gè)參數(shù)是你要寫(xiě)入的字符,第二個(gè)是你要寫(xiě)入的文件流。

讀寫(xiě)整個(gè)字符串

這里就要用到fgets和fputs兩個(gè)函數(shù)了。

#include <stdio.h>...charfgets(char* s, int size, FILE* stream);int fputs(const chat* s, FILE* stream);

其中,fgets有三個(gè)參數(shù),第一個(gè)是一個(gè)字符型指針,用來(lái)存放讀取的數(shù)據(jù);第二個(gè)用來(lái)指定讀取的長(zhǎng)度(包含'\0');第三個(gè)是用于指定讀取的文件流。

函數(shù)調(diào)用成功后,會(huì)返回第一個(gè)參數(shù)所指向的地址。如果讀取到EOF則eof指示器被設(shè)置。若一開(kāi)始就讀取到EOF,第一個(gè)參數(shù)的內(nèi)容不變,返回NULL。若讀取發(fā)生錯(cuò)誤,則error指示器被設(shè)置,函數(shù)返回NULL,第一個(gè)參數(shù)內(nèi)容可能會(huì)被改變。

fputs第一個(gè)參數(shù)用于存放待寫(xiě)入的數(shù)據(jù),第二個(gè)是指定待寫(xiě)入的文件流。函數(shù)調(diào)用成功,返回一個(gè)非 0 值,失敗則返回EOF。

格式化讀寫(xiě)文件

在文件里,我們就不能用我們熟悉的scanf和printf了。但是C語(yǔ)言也提供一組類(lèi)似的函數(shù):fscanf和fprintf。

用法上,第一個(gè)參數(shù)用于指定文件流,后面的就是照搬的scanf和printf中的參數(shù)。

//Example 02#include <stdio.h>#include <stdlib.h>#include <time.h>int main(void){    FILE* fp;    struct  tmp;    time_t t;        time(&t);    p = localtime(&t);    //寫(xiě)入日期到文件    if ((fp = fopen('date.txt''w')) == NULL)    {        printf('打開(kāi)文件失敗!\n');        exit(EXIT_FAILURE);    }    fprintf(fp, '%d-%d-%d'1900 + p -> tm_year, 1 + p -> tm_mon, p -> tm_mday);    fclose(fp);    //讀取文件日期,輸出到終端    int year, month, day;    if ((fp = fopen('date.txt''r')) == NULL)    {        printf('打開(kāi)文件失?。n');        exit(EXIT_FAILURE);    }    fscanf(fp, '%d-%d-%d', &year, &month, &day);    printf('%d-%d-%d\n', year, month, day);    fclose(fp);    return 0;}
//date.txt中的內(nèi)容2020-6-15
//Consequence 022020-6-15

二進(jìn)制讀寫(xiě)

我們用fopen函數(shù)可以用二進(jìn)制的方式來(lái)打開(kāi)一個(gè)文件,但實(shí)際上我們要用二進(jìn)制的方式來(lái)讀寫(xiě),還得用相應(yīng)的函數(shù)才行。

C語(yǔ)言提供了fread和fwrite兩個(gè)函數(shù)來(lái)實(shí)現(xiàn)二進(jìn)制的讀取和寫(xiě)入。

#include <stdio.h>...size_t fread(void* ptr, size_t size, size_t nmemb, FILE* stream);size_t fwrite(const void* ptr, size_t size, size_t nmemb, FILE* stream);

首先來(lái)看fread。這個(gè)函數(shù)有四個(gè)參數(shù)。第一個(gè)指向存放數(shù)據(jù)的地址,第二個(gè)指定讀取的每個(gè)元素的尺寸,第三個(gè)指定準(zhǔn)備讀取的元素個(gè)數(shù),最后一個(gè)指向待讀取的文件流。

函數(shù)調(diào)用成功,會(huì)返回讀取到的元素個(gè)數(shù),如果實(shí)際讀取的比第三個(gè)參數(shù)小,那么可能會(huì)一直讀取到文件末尾或者發(fā)生錯(cuò)誤,這種情況就要通過(guò)foef和ferror來(lái)進(jìn)一步判斷。

然后是fwrite,也是有四個(gè)參數(shù)。第一個(gè)是指向存放數(shù)據(jù)的地址,第二個(gè)是指定待寫(xiě)入的每個(gè)元素的尺寸,第三個(gè)是指定待寫(xiě)入的元素的個(gè)數(shù),最后一個(gè)是指向待寫(xiě)入的文件流。

隨機(jī)讀寫(xiě)文件

剛剛我們介紹的,都是從文件頭開(kāi)始讀寫(xiě)。但是我們實(shí)際生產(chǎn)生活中,很多時(shí)候我們是需要任意修改的。比如改一個(gè)文檔,很有可能是中間的什么地方錯(cuò)了,或者是表達(dá)有不妥。那么這個(gè)時(shí)候如果你還要從頭開(kāi)始去檢索,那樣效率就太低了。

于是,C語(yǔ)言也為我們提供了這個(gè)功能,就是隨機(jī)讀寫(xiě)。

首先,我們要了解光標(biāo)的位置,才能夠更好地運(yùn)用這個(gè)功能。C語(yǔ)言為我們提供了ftell函數(shù),它可以告訴我們現(xiàn)在的光標(biāo)位置。

#include <stdio.h>...long ftell(FILE* stream);

如果將一個(gè)文件看成一個(gè)數(shù)組,那么這個(gè)函數(shù)返回的就是這個(gè)數(shù)組的下標(biāo)。

//Example 01#include <stdio.h>#include <stdlib.h>int main(void){    FILE* fp;    if ((fp = fopen('data.txt''w')) == NULL)    {        printf('文件打開(kāi)失??!\n');        exit(EXIT_FAILURE);    }    printf('%ld\n', ftell(fp));    fputc('T', fp);    printf('%ld\n', ftell(fp));    fputs('echZone\n', fp);    printf('%ld\n', ftell(fp));    fclose(fp);        return 0;}
//data.txt中的內(nèi)容TechZone
//Consequence 010110

如果你想將光標(biāo)快速移動(dòng)到文件頭,可以用rewind函數(shù)來(lái)實(shí)現(xiàn)。

...rewind(fp);fputs('Hello', fp);fclose(fp);...
//data.txt中的內(nèi)容Helloone

可以看到,它會(huì)覆蓋我們前面的數(shù)據(jù)。

有的同學(xué)可能會(huì)說(shuō)了,你這不還是沒(méi)解決問(wèn)題嗎?

好的,那就來(lái)解決下問(wèn)題吧。C語(yǔ)言給我們提供了一個(gè)函數(shù)fseek,這個(gè)函數(shù)可以直接把光標(biāo)跳轉(zhuǎn)到我們想要的位置。

#include <stdio.h>...int fseek(FILE* stream, long int offset, int whence);

第一個(gè)參數(shù)是指的我們要讀取的文件流,第二個(gè)是偏移量(往后走是正數(shù),往前走是負(fù)數(shù)),第三個(gè)是指的開(kāi)始偏移的位置。

值描述SEEK_SET文件開(kāi)頭SEEK_CUR當(dāng)前位置SEEK_END文件末尾

如果我要定位到第一百個(gè)字符的位置,那么:

fseek(fp, 100, SEEK_SET)

倒數(shù)第 10 個(gè)就要這樣:

fseek(fp, -10, SEEK_END)

標(biāo)準(zhǔn)流

標(biāo)準(zhǔn)輸入,標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯(cuò)誤輸出

一般C語(yǔ)言程序在執(zhí)行的時(shí)候,都會(huì)有 3 個(gè)面向終端的文件流,分別是「標(biāo)準(zhǔn)輸入」,「標(biāo)準(zhǔn)輸出」「標(biāo)準(zhǔn)錯(cuò)誤輸出」。我們之前用printf的時(shí)候,其實(shí)就是在往標(biāo)準(zhǔn)輸出流中寫(xiě)入字符串;用scanf的時(shí)候,其實(shí)就是函數(shù)在從標(biāo)準(zhǔn)輸入流中讀取字符串。當(dāng)然,我們寫(xiě)的程序也不可能一直都是正確的,警告和報(bào)錯(cuò)的情況時(shí)有發(fā)生,這個(gè)時(shí)候其實(shí)就是對(duì)標(biāo)準(zhǔn)錯(cuò)誤輸出中寫(xiě)入數(shù)據(jù)。

這三個(gè)流,我們就將它們稱(chēng)為:「標(biāo)準(zhǔn)流」

C語(yǔ)言分別為這三個(gè)標(biāo)準(zhǔn)流提供了對(duì)應(yīng)的文件指針:stdin,stdout,stderr

比如打開(kāi)文件失敗的時(shí)候,就可以這樣顯示:

...    fputs('打開(kāi)文件失敗!\n'stderr); exit(EXIT_FAILURE);...

這樣就不用printf這種“不專(zhuān)業(yè)”的錯(cuò)誤指示方法了。

打開(kāi)文件失??!

錯(cuò)誤處理

每個(gè)流的內(nèi)部都有兩個(gè)指示器。一個(gè)是「文件結(jié)束指示器feof」,當(dāng)遇到文件末尾時(shí)被設(shè)置;另一個(gè)是「錯(cuò)誤指示器ferror」,當(dāng)讀寫(xiě)文件出錯(cuò)時(shí)被設(shè)置。

...if (ferror(fp)){    fputs('出錯(cuò)了!\n'stderr);}...

而使用clearerr可以人為地清除兩個(gè)指示器的狀態(tài):

...    clearerr(fp);...

錯(cuò)誤指示器只能判斷是否出了錯(cuò)誤,但具體是什么錯(cuò)誤,那就要看errno和perror了。

首先看errno。這個(gè)函數(shù)包含在errno.h這個(gè)頭文件中。它會(huì)返回一個(gè)錯(cuò)誤碼。

#include <errno.h>...printf('打開(kāi)文件失?。?d\n', errno);...

舉個(gè)例子:

打開(kāi)文件失?。?

但是這個(gè)錯(cuò)誤代碼不是所有人都知道它的含義。所以C語(yǔ)言又提供了一個(gè)函數(shù)perror,它可以直接用文字來(lái)提示我們錯(cuò)誤的地方。

#include <stdio.h>...perror('打開(kāi)文件失敗,原因是');...

結(jié)果是這樣的:

打開(kāi)文件失敗,原因是:No such file or directory

中間的冒號(hào)是自動(dòng)加上的。

或許以后在你的開(kāi)發(fā)生涯中,用的最多的不是C語(yǔ)言,但這門(mén)語(yǔ)言對(duì)你帶來(lái)的提升,那是不可忽視的。最后,祝各位學(xué)有所成!

關(guān)注我,帶你遨游代碼的世界

獲取完整視頻教程,可以關(guān)注B站:https://www.bilibili.com/video/BV1QE411y7v4

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶(hù)發(fā)布,不代表本站觀(guān)點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶(hù) 評(píng)論公約

    類(lèi)似文章 更多

    出差被公高潮久久中文字幕| 亚洲精品中文字幕欧美| 亚洲深夜精品福利一区| 黑人巨大精品欧美一区二区区| 国产成人国产精品国产三级| 色婷婷国产熟妇人妻露脸| 一区二区三区18禁看| 五月婷婷六月丁香在线观看| 激情综合网俺也狠狠地| 国内自拍偷拍福利视频| 欧美一级片日韩一级片| 国产精品一级香蕉一区| 美女极度色诱视频在线观看| 久久机热频这里只精品| av免费视屏在线观看| 日韩欧美国产精品中文字幕| 亚洲一区精品二人人爽久久| 绝望的校花花间淫事2| 精品香蕉一区二区在线| 国产午夜精品福利免费不| 99久久精品免费看国产高清| 久草视频在线视频在线观看| 色婷婷人妻av毛片一区二区三区| 国产三级不卡在线观看视频| 国产又大又硬又粗又黄| 亚洲中文字幕亲近伦片| 精品国产丝袜一区二区| 亚洲最新的黄色录像在线| 日本av在线不卡一区| 东京干男人都知道的天堂| 人妻亚洲一区二区三区| 暴力性生活在线免费视频| 91人妻久久精品一区二区三区| 不卡视频在线一区二区三区| 99视频精品免费视频| 中文字日产幕码三区国产| 人妻内射在线二区一区| 白丝美女被插入视频在线观看| 国语对白刺激高潮在线视频| 国产一级片内射视频免费播放| 亚洲精品中文字幕熟女|