天天看點

【C語言進階】—— 檔案操作(詳解)⌛前言⌛一、什麼是檔案二、檔案緩沖區三、檔案指針✨四、檔案操作

@toc

在之前的學習過程中,我們所編寫的程式在執行過程中所産生的資料及結果都隻是臨時存放在記憶體區域,一旦程式運作結束,該程式所涉及的記憶體空間全部傳回給作業系統。這時候如果我們想要去檢視這些資料和結果,顯然是做不到的!

那有沒有什麼方法能夠解決這個問題呢?也就是說可以将程式運作過程所産生的過程資料和結果資料都儲存起來,即便程式結束,我們也可以找到這些内容,甚至是在之後所寫的其他程式中也能繼續使用這些内容。

答案當然是-- - 有,這也就是我們這篇文章所要讨論的東西–檔案。

【C語言進階】—— 檔案操作(詳解)⌛前言⌛一、什麼是檔案二、檔案緩沖區三、檔案指針✨四、檔案操作

1、站在程式設計角度,<code>檔案有兩種:程式檔案、資料檔案</code>

程式檔案: 包括源程式檔案(字尾為.c),目标檔案(windows環境字尾為.obj),可執行程式(windows環境字尾為.exe)。 資料檔案: 檔案的内容不一定是程式,而是程式運作時讀寫的資料,比如程式運作需要從中讀取資料的檔案,或者輸出内容的檔案。檔案名

2、檔案名

一個檔案要有一個唯一的檔案辨別,以便使用者識别和引用。 檔案名包含3部分:檔案路徑+檔案名主幹+檔案字尾 例如:<code>c:\code\test.txt</code> 檔案路徑:c : \code 檔案名主幹:test 檔案字尾:.txt 為了友善起見,檔案辨別常被稱為檔案名

3、檔案類型

根據資料的組織形式,資料檔案被稱為<code>文本檔案</code>或者<code>二進制檔案</code>。

資料在記憶體中以二進制的形式存儲,如果不加轉換的輸出到外存,就是二進制檔案。 如果要求在外存上以ascii碼的形式存儲,則需要在存儲前轉換。以ascii字元的形式存儲的檔案就是文本檔案。

4、檔案存儲

一個資料在記憶體中存儲時,字元一律以ascii形式存儲,數值型資料既可以用ascii形式存儲,也可以使用二進制形式存儲

10000(十進制)在記憶體中的存儲圖解示例:

【C語言進階】—— 檔案操作(詳解)⌛前言⌛一、什麼是檔案二、檔案緩沖區三、檔案指針✨四、檔案操作

字元一律以ascii形式存儲,數值型資料既可以用ascii形式存儲,也可以使用二進制形式存儲。

如有整數10000,如果以ascii碼的形式輸出到磁盤,則磁盤中占用5個位元組(每個字元一個位元組),而二進制形式輸 出,則在磁盤上隻占4個位元組(vs2013測試)。

【C語言進階】—— 檔案操作(詳解)⌛前言⌛一、什麼是檔案二、檔案緩沖區三、檔案指針✨四、檔案操作
【C語言進階】—— 檔案操作(詳解)⌛前言⌛一、什麼是檔案二、檔案緩沖區三、檔案指針✨四、檔案操作

<code>ansic标準采用"緩沖檔案系統"處理的資料檔案的</code>,所謂緩沖檔案系統是指系統自動地在記憶體中為程式中每一個正在使用的檔案開辟一塊"檔案緩沖區"。從記憶體向磁盤輸出資料會先送到記憶體中的緩沖區,裝滿緩沖區後才一起送到磁盤上。如果從磁盤向計算機讀入資料,則從磁盤檔案中讀取資料輸入到記憶體緩沖區(充滿緩沖區),然後再從緩沖區逐個地将資料送到程式資料區(程式變量等)。緩沖區的大小根據c編譯系統決定的。

【C語言進階】—— 檔案操作(詳解)⌛前言⌛一、什麼是檔案二、檔案緩沖區三、檔案指針✨四、檔案操作

将以下代碼放到linux下跑一下,就可以清楚的感覺到緩沖區的作用。

緩沖檔案系統中, 關鍵的概念是“檔案類型指針”, 簡稱“檔案指針”。每個被使用的檔案都在記憶體中開辟一個相應的檔案資訊區,用來存放檔案的有關資訊(如檔案的名字、檔案狀态及檔案目前位置等)。這些資訊是儲存在一個結構體變量中的。 該結構體類型是由系統聲明的,取名為file。

例如: vs2013編譯環境提供的stdio.h頭文作中有以下的檔案類型聲明:

(注意:以上代碼了解即可,有興趣可自行研究) 不同的c編譯器的file類型包含的内容不完全相同,但是大同小異。 每當打開一個檔案的時候,系統會根據檔案的情況自動建立一個file結構的變量,并填充其中的資訊,使用者不必關心細節。 一般都是通過一個file的指針來維護這個file結構的變量,這樣使用起來更加友善。 下面我們可以建立─個file* 的指針變量:

定義pf是一個指向file類型資料的指針變量。可以使pf指向某個檔案的檔案資訊區(是一個結構體變量)。通過該檔案資訊區中的資訊就能夠通路該檔案。也就是說,通過檔案指針變量能夠找到與它關聯的檔案

比如:

【C語言進階】—— 檔案操作(詳解)⌛前言⌛一、什麼是檔案二、檔案緩沖區三、檔案指針✨四、檔案操作

檔案在讀寫之前應該先打開檔案,在使用結束之後應該關閉檔案。

在編寫程式的時候,在打開檔案的同時,都會傳回一個file* 的指針變量指向該檔案,也相當于建立了

指針和檔案的關系。

ansic規定使用fopen函數來打開檔案,fclose來關閉檔案。

函數原型:file fopen(const char filename, const char* mode); 函數功能:open a file.(打開檔案) 傳回類型:each of these functions returns a pointer to the open file.a null pointer value indicates an error.(如果打開成功,傳回指向檔案資訊區的指針,如果傳回失敗,傳回空指針null) 函數參數1:filename(檔案名,實際上包括3部分内容,而不僅僅是檔案名主幹。如果檔案路徑未寫,則預設本路徑) 函數參數2:type of access permitted(檔案打開方式)

檔案打開方式如下表:

【C語言進階】—— 檔案操作(詳解)⌛前言⌛一、什麼是檔案二、檔案緩沖區三、檔案指針✨四、檔案操作
注意:‘w’打開的時候如果有同名檔案,該檔案中的内容會被銷毀
函數原型:int fclose(file* stream); 函數功能:closes a stream(fclose) or closes all open streams(_fcloseall).(關閉檔案) 傳回類型:fclose returns 0 if the stream is successfully closed._fcloseall returns the total number of streams closed.both functions return eof to indicate an error(關閉檔案成功傳回0,關閉檔案失敗傳回eof(值為 - 1)來報錯) 函數參數:pointer to file structure(檔案指針)

舉例:

1)在項目工程所在檔案路徑下建立一個測試檔案

【C語言進階】—— 檔案操作(詳解)⌛前言⌛一、什麼是檔案二、檔案緩沖區三、檔案指針✨四、檔案操作

2)測試代碼

3)檔案打開結果

【C語言進階】—— 檔案操作(詳解)⌛前言⌛一、什麼是檔案二、檔案緩沖區三、檔案指針✨四、檔案操作

調整一下(為了進一步了解相對路徑和絕對路徑,這裡的filename使用絕對路徑的方式)

【C語言進階】—— 檔案操作(詳解)⌛前言⌛一、什麼是檔案二、檔案緩沖區三、檔案指針✨四、檔案操作

剛剛我們是以“讀”的方式來打開檔案,現在我們來嘗試用“寫”的方式來打開檔案

在我們剛剛的路徑下是沒有file1.dat這個檔案的,因為我們<code>用“w”的方式打開檔案,是以會建立一個新的檔案</code>

【C語言進階】—— 檔案操作(詳解)⌛前言⌛一、什麼是檔案二、檔案緩沖區三、檔案指針✨四、檔案操作
【C語言進階】—— 檔案操作(詳解)⌛前言⌛一、什麼是檔案二、檔案緩沖區三、檔案指針✨四、檔案操作
【C語言進階】—— 檔案操作(詳解)⌛前言⌛一、什麼是檔案二、檔案緩沖區三、檔案指針✨四、檔案操作

(1)舉例 :fput寫入字元

【C語言進階】—— 檔案操作(詳解)⌛前言⌛一、什麼是檔案二、檔案緩沖區三、檔案指針✨四、檔案操作

寫入成功

【C語言進階】—— 檔案操作(詳解)⌛前言⌛一、什麼是檔案二、檔案緩沖區三、檔案指針✨四、檔案操作

(2)舉例 :fgetc寫入字元

成功輸出

【C語言進階】—— 檔案操作(詳解)⌛前言⌛一、什麼是檔案二、檔案緩沖區三、檔案指針✨四、檔案操作
輸入輸出是資料傳送的過程, 資料如流水一樣從一處流向另一處, 是以常将輸入輸出形象地稱為流(stream), 即資料流。

流表示了資訊從源到目的端的流動。在輸入操作時, 資料從檔案流向計算機記憶體, 在輸出操作時, 資料從計算機流向檔案(如列印機、磁盤檔案)。檔案是由作業系統進行統一管理的, 無論是用word打開或儲存檔案, 還是c程式中的輸入輸出都是通過作業系統進行的。“流”是一個傳輸通道, 資料可以從運作環境(有關裝置)流入程式中, 或從程式流至運作環境。

c語言把檔案看作一個字元(或位元組)的序列, 即由一個一個字元(或位元組)的資料順序組成。一個輸入輸出流就是一個字元流或位元組(内容為二進制資料)流。 c的資料檔案由一連串的字元(或位元組)組成, 而不考慮行的界限, 兩行資料間不會自動加分隔符,對檔案的存取是以字元(位元組)為機關的。輸入輸出資料流的開始和結束僅受程式控制而不受實體符号(如回車換行符)控制, 這就增加了處理的靈活性。 這種檔案稱為流式檔案。
【C語言進階】—— 檔案操作(詳解)⌛前言⌛一、什麼是檔案二、檔案緩沖區三、檔案指針✨四、檔案操作
*c語言程式,隻要運作起來,就預設打開了三個流,類型均為file <code>stdin-- - 标準輸入流-- - 鍵盤&lt;br/&gt;stdout-- - 标準輸出流-- - 螢幕&lt;br/&gt;stderr-- - 标準錯誤流-- - 螢幕</code>
【C語言進階】—— 檔案操作(詳解)⌛前言⌛一、什麼是檔案二、檔案緩沖區三、檔案指針✨四、檔案操作

知道這個概念有個想法,他就可以從鍵盤上輸入然後螢幕讀出來

看下面代碼例子:

【C語言進階】—— 檔案操作(詳解)⌛前言⌛一、什麼是檔案二、檔案緩沖區三、檔案指針✨四、檔案操作
【C語言進階】—— 檔案操作(詳解)⌛前言⌛一、什麼是檔案二、檔案緩沖區三、檔案指針✨四、檔案操作

接下來我們進行讀檔案,将生成的檔案讀出來

【C語言進階】—— 檔案操作(詳解)⌛前言⌛一、什麼是檔案二、檔案緩沖區三、檔案指針✨四、檔案操作

剛剛這種是從檔案裡面讀,我們也可以從标準輸入流stdin–鍵盤讀

【C語言進階】—— 檔案操作(詳解)⌛前言⌛一、什麼是檔案二、檔案緩沖區三、檔案指針✨四、檔案操作

上面說的fputc\fgetc都是一個個字元方式讀取的,如果要用這兩個函數來處理字元串的話,效率就會非常低,那麼用沒用類似于這兩個函數,但是可以一行一行讀取的函數呢?

【C語言進階】—— 檔案操作(詳解)⌛前言⌛一、什麼是檔案二、檔案緩沖區三、檔案指針✨四、檔案操作

這兩個函數就是用來實作剛剛說的功能的。

舉例fgets :

1)我們在文檔中輸入

【C語言進階】—— 檔案操作(詳解)⌛前言⌛一、什麼是檔案二、檔案緩沖區三、檔案指針✨四、檔案操作

2)我們使用fgets讀取檔案資訊

【C語言進階】—— 檔案操作(詳解)⌛前言⌛一、什麼是檔案二、檔案緩沖區三、檔案指針✨四、檔案操作

舉例二 :fputs輸入資料到檔案中

1)使用fputs輸入 hello world

2 )文本裡面内容被修改

【C語言進階】—— 檔案操作(詳解)⌛前言⌛一、什麼是檔案二、檔案緩沖區三、檔案指針✨四、檔案操作

注意:

<code>如果想要寫入的字元串換行,則需要在字元串内容中添加換行‘\n’</code>

【C語言進階】—— 檔案操作(詳解)⌛前言⌛一、什麼是檔案二、檔案緩沖區三、檔案指針✨四、檔案操作

舉例3 : 從鍵盤上讀取一行文本

【C語言進階】—— 檔案操作(詳解)⌛前言⌛一、什麼是檔案二、檔案緩沖區三、檔案指針✨四、檔案操作
【C語言進階】—— 檔案操作(詳解)⌛前言⌛一、什麼是檔案二、檔案緩沖區三、檔案指針✨四、檔案操作

前面進行的是字元的輸入輸出, 而實際上資料的類型是豐富的。大家已很熟悉用printf 函數和scanf 函數向終端進行格式化的輸入輸出, 即用各種不同的格式以終端為對象輸入輸出資料。其實也可以對檔案進行格式化輸入輸出, 這時就要用fprintf函數和 fscanf函數, 從函數名可以看到, 它們隻是在printf和 scanf的前面加了一個字母f。它們的作用與printf 函數和scanf函數相仿, 都是格式化讀寫函數。

<code>隻有一點不同: fprintf 和 fscanf 函數的讀寫對象不是終端而是檔案。</code>

它們的一般調用方式為 :

fprintf(檔案指針, 格式字元串, 輸出表列); fscanf(檔案指針, 格式字元串, 輸入表列);

函數格式就是這樣

就比printf前面多了一個流

1)舉例1 :fprintf函數寫資料到檔案裡

結果展示:

【C語言進階】—— 檔案操作(詳解)⌛前言⌛一、什麼是檔案二、檔案緩沖區三、檔案指針✨四、檔案操作

2)舉例2:fscanf(讀取)函數得到檔案資料

現在我們将輸出的結構體檔案重新讀入到結構體變量中:

【C語言進階】—— 檔案操作(詳解)⌛前言⌛一、什麼是檔案二、檔案緩沖區三、檔案指針✨四、檔案操作

當然我們也可以用fprintf,stdout是标準輸出流,也就是螢幕

【C語言進階】—— 檔案操作(詳解)⌛前言⌛一、什麼是檔案二、檔案緩沖區三、檔案指針✨四、檔案操作

上面我們說談到的輸入和輸出實際上都是以文本資料的形式,也就是ascii碼形式。

在程式中不僅需要一次輸入輸出一個資料, 而且常常需要一次輸入輸出一組資料(如數組或結構體變量的值),c語言允許用fread函數從檔案中讀一個資料塊, 用fwrite函數向檔案寫一個資料塊。在讀寫時是以二進制形式進行的。在向磁盤寫資料時, 直接将記憶體中一組資料原封不動、不加轉換地複制到磁盤檔案上, 在讀入時也是将磁盤檔案中若幹位元組的内容一批讀入記憶體。

函數原型 :

1)<code>buffer:是一個位址</code>。 對fread來說, 它是用來存放從檔案讀入的資料的存儲區的位址。 對fwrite來說,是要把此位址開始的存儲區中的資料向檔案輸出(以上指的是起始位址)。 2)<code>size : 要讀寫的位元組數</code>。 3)<code>count : 要讀寫多少個資料項(每個資料項長度為size)</code>。 4) <code>fp : file類型指針</code>。 在打開檔案時指定用二進制檔案, 這樣就可以用fread和 fwrite函數讀寫任何類型的資訊。

舉例(一) :fwrite函數寫入二進制資料進檔案中

【C語言進階】—— 檔案操作(詳解)⌛前言⌛一、什麼是檔案二、檔案緩沖區三、檔案指針✨四、檔案操作

舉例(二) :fread函數讀取檔案中的二進制,并列印

【C語言進階】—— 檔案操作(詳解)⌛前言⌛一、什麼是檔案二、檔案緩沖區三、檔案指針✨四、檔案操作

scanf / fscanf / sscanf

printf / fprintf / sprintf

scanf與printf是一組-- - 從标準輸入流(stdin) / 輸出流(stdout)中輸入 / 輸出格式化的資料 fscanf與fprintf一組-- - 從所有流類型(包括标準輸入流\輸出流,檔案流等各種流)輸入 / 輸出格式化的資料 sscanf與sprintf一組-- - 從一個字元串中輸入 / 輸出格式化資料

函數原型

sscanf 與 sprintf 使用舉例:

【C語言進階】—— 檔案操作(詳解)⌛前言⌛一、什麼是檔案二、檔案緩沖區三、檔案指針✨四、檔案操作

一般情況下, 在對字元檔案進行順序讀寫時, 檔案位置标記指向檔案開頭, 這時如果對檔案進行讀的操作, 就讀第1個字元, 然後檔案位置标記向後移一個位置, 在下一次執行讀的操作時, 就将位置标記指向的第⒉個字元讀入。依此類推, 遇到檔案尾結束。

【C語言進階】—— 檔案操作(詳解)⌛前言⌛一、什麼是檔案二、檔案緩沖區三、檔案指針✨四、檔案操作
可以根據讀寫的需要, 人為地移動檔案位置标記的位置。檔案位置标記可以向前移、向後移, 移到檔案頭或檔案尾, 然後對該位置進行讀寫, 顯然這就不是順序讀寫了, 而是随機讀寫。
<code>根據檔案指針的位置和偏移量來定位檔案指針。</code>

函數原型:

【C語言進階】—— 檔案操作(詳解)⌛前言⌛一、什麼是檔案二、檔案緩沖區三、檔案指針✨四、檔案操作

起始點的選項有三個

【C語言進階】—— 檔案操作(詳解)⌛前言⌛一、什麼是檔案二、檔案緩沖區三、檔案指針✨四、檔案操作

1)準備檔案,輸入一串字元

【C語言進階】—— 檔案操作(詳解)⌛前言⌛一、什麼是檔案二、檔案緩沖區三、檔案指針✨四、檔案操作

2)使用 fseek函數

3)運作結果

【C語言進階】—— 檔案操作(詳解)⌛前言⌛一、什麼是檔案二、檔案緩沖區三、檔案指針✨四、檔案操作

現在我們運用fseek函數對目前檔案指針位置進行偏移,

【C語言進階】—— 檔案操作(詳解)⌛前言⌛一、什麼是檔案二、檔案緩沖區三、檔案指針✨四、檔案操作
本來pf讀了a之後,應該移動到b的位置,但是用fseek讓其偏移 - 1,也就是向左偏移一個位置(一個位元組),又回到了a。

當起始點為seek_end 的時候,隻能向左(向前)偏移,也就是偏移量隻能為負值

當起始點為seek_set 的時候,隻能向右(向後)偏移,也就是偏移量隻能為正值

<code>傳回檔案指針相對于起始位置的偏移量</code>
【C語言進階】—— 檔案操作(詳解)⌛前言⌛一、什麼是檔案二、檔案緩沖區三、檔案指針✨四、檔案操作
<code>讓檔案指針的位置回到檔案的起始位置</code>

舉例 ;

【C語言進階】—— 檔案操作(詳解)⌛前言⌛一、什麼是檔案二、檔案緩沖區三、檔案指針✨四、檔案操作
常被被錯誤使用的<code>feof函數</code>

牢記 : 在檔案讀取過程中,不能用feof函數的傳回值直接用來判斷檔案的是否結束。而是應用于當檔案讀取結束的時候,判斷是讀取失敗結束,還是遇到檔案尾結束。

文本檔案讀取是否結束,判斷傳回值是否為eof (fgetc),或者null(fgets)

例如:

<code>fgetc判斷是否為eof.&lt;br/&gt;fgets判斷傳回值是否為null.</code>

二進制檔案的讀取結束判斷,判斷傳回值是否小于實際要讀的個數。

<code>fread判斷傳回值是否小于實際要讀的個數</code>

例如∶

1.fread判斷傳回值是否小于實際要讀的個數。 fgetc函數在讀取結束的時候,會傳回eof 正常讀取的時候,傳回的是字元的ascii碼值 2.fgets函數在讀取結束的時候,會傳回null 正常讀取的是時候,傳回存放字元串的空間起始位址。 3.fread函數在讀取的時候,傳回的是實際讀取到的完整元素的個數,如果發現讀取到的完整元素的個數小于實際要讀取(指定要讀取)的個數,這就是最後一次讀取了。

(1)先建立一個test.txt檔案,然後在裡面随便編輯點内容進行儲存

【C語言進階】—— 檔案操作(詳解)⌛前言⌛一、什麼是檔案二、檔案緩沖區三、檔案指針✨四、檔案操作

2)編寫代碼,将剛剛test.txt檔案的内容讀出來放到一個新檔案test2.txt當中(相當于複制拷貝一份test.txt)

程式運作後,在源檔案路徑下生成test2.txt

【C語言進階】—— 檔案操作(詳解)⌛前言⌛一、什麼是檔案二、檔案緩沖區三、檔案指針✨四、檔案操作

打開test.txt和test2.txt進行對比,會發現兩者是一樣的

【C語言進階】—— 檔案操作(詳解)⌛前言⌛一、什麼是檔案二、檔案緩沖區三、檔案指針✨四、檔案操作

代碼舉例:文本檔案

代碼舉例:二進制檔案

【C語言進階】—— 檔案操作(詳解)⌛前言⌛一、什麼是檔案二、檔案緩沖區三、檔案指針✨四、檔案操作

繼續閱讀