【C】C語言中文件操做相關內容

 

1. 文件和流的關係

  C將每一個文件簡單地做爲順序字節流。每一個文件用文件結束符結束,或者在特定字節數的地方結束,這個特定的字節數能夠存儲在系統維護的管理數據結構中。當打開文件時,就創建了和文件的關係。html

  在開始執行程序的時候,將自動打開3個文件和相關的流:標準輸入流、標準輸出流和標準錯誤。流提供了文件和程序的通訊通道。打開一個文件將返回指向FILE結構(在stdio.h中定義)的指針,它包含用於處理文件的信息,也就是說,這個結構包含文件描述符。文件描述符是操做系統數組(打開文件列表的索引)。每一個數組元素包含一個文件控制塊(FCB, File Control Block),操做系統用它來管理特定的文件。程序員

  標準輸入、標準輸出和標準錯誤是用文件指針stdin、stdout和stderr來處理的。數組

  C語言把磁盤文件當作是字符(或字節)的序列,按照存儲信息的形式來講,文件主要是有文本文件和二進制文件。文本文件由一個個字符組成,每一個字節存放一個ASCII碼制,表明一個字符。二進制文件把內存中的數據按其在內存中的存儲形式原樣放入磁盤空間。數據結構

二進制文件以及文本文件均可以看作是「數據流」。app

2. C語言文件管理的實現

  C程序用不一樣的FILE結構管理每一個文件。程序員可使用文件,但不須要知道FILE結構的細節。實際上,FILE結構是間接地操做系統的文件控制塊(FCB)來實現對文件的操做的。例如FILE結構體中的_file實際上就是一個文件描述符,做爲進入打開文件表索引的整數。函數

3. 操做系統文件管理簡介

  文件是存放在物理磁盤上的,包括文件控制塊(FCB)和數據塊。文件控制塊一般包括文件權限、日期(建立、讀取、修改)、擁有者、文件大小、數據塊信息。數據塊用來存儲實際的內容。對於打開的文件,操做系統是這樣管理的:編碼

1spa

系統維護了兩張表,一張是系統級打開文件表,一張是進程級打開文件表(每一個進程有一個)。操作系統

  系統級打開文件表複製了文件控制塊的信息等;進程級打開文件表保存了指向系統級文件表的指針及其餘信息。指針

  系統級文件表每一項都保存一個計數器,即該文件打開的次數。咱們初次打開一個文件時,系統首先查看該文件是否已在系統級文件表中,若是不在,則建立該項信息,不然,計數器加1。當咱們關閉一個文件時,相應的計數也會減1,當減到0時,系統將系統級文件表中的項刪除。

  進程打開一個文件時,會在進程級文件表中添加一項。每項的信息包括當前文件偏移量(讀寫文件的位置)、存取權限、和一個指向系統級文件表中對應文件項的指針。系統級文件表中的每一項經過文件描述符(一個非負整數)來標識。

  FILE結構體中的_file成員應該是指向進程級打開文件表,而後,經過進程級打開文件表能夠找到系統級打開文件表,進而能夠經過FCB操做物理磁盤上面的文件。

  每打開一次文件,哪怕屢次打開的都是同一個文件,進程級打開文件表中應該都會添加一個記錄。若是是打開的是同一個文件,這多條記錄對應着同一個物理磁盤文件。因爲每一次打開文件所進行的操做都是經過進程級打開文件表中不一樣的記錄來實現的,這樣,至關於每次打開文件的操做是相對獨立的。

4. 緩衝區

  當咱們從鍵盤輸入數據的時候,數據並非直接被咱們獲得,而是放在了緩衝區中,而後咱們從緩衝區中獲得咱們想要的數據 。若是咱們經過setbuf()setvbuf()函數將緩衝區設置10個字節的大小,而咱們從鍵盤輸入了20個字節大小的數據,這樣咱們輸入的前10個數據會放在緩衝區中,由於咱們設置的緩衝區的大小隻可以裝下10個字節大小的數據,裝不下20個字節大小的數據。那麼剩下的那10個字節大小的數據怎麼辦呢?暫時放在了輸入流中。請看下圖:

 

  上面的箭頭表示的區域就至關是一個輸入流,紅色的地方至關於一個開關,這個開關能夠控制往深綠色區域(標註的是緩衝區)裏放進去的數據,輸入20個字節的數據只往緩衝區中放進去了10個字節,剩下的10個字節的數據就被停留在了輸入流裏!等待下去往緩衝區中放入!接下來系統是如何來控制這個緩衝區呢?
再說一下 FILE 結構體中幾個相關成員的含義:
     cnt  // 剩餘的字符,若是是輸入緩衝區,那麼就表示緩衝區中還有多少個字符未被讀取
     ptr  // 下一個要被讀取的字符的地址
     base  // 緩衝區基地址
  在上面咱們向緩衝區中放入了10個字節大小的數據,FILE結構體中的 cnt 變爲了10 ,說明此時緩衝區中有10個字節大小的數據能夠讀,同時咱們假設緩衝區的基地址也就是 base 是0x00428e60 ,它是不變的 ,而此時 ptr 的值也爲0x00428e60 ,表示從0x00428e60這個位置開始讀取數據,當咱們從緩衝區中讀取5個數據的時候,cnt 變爲了5 ,表示緩衝區還有5個數據能夠讀,ptr 則變爲了0x0042e865表示下次應該從這個位置開始讀取緩衝區中的數據 ,若是接下來咱們再讀取5個數據的時候,cnt 則變爲了0 ,表示緩衝區中已經沒有任何數據了,ptr 變爲了0x0042869表示下次應該從這個位置開始從緩衝區中讀取數據,可是此時緩衝區中已經沒有任何數據了,因此要將輸入流中的剩下的那10個數據放進來,這樣緩衝區中又有了10個數據,此時 cnt 變爲了10 ,注意了剛纔咱們講到 ptr 的值是0x00428e69 ,而當緩衝區中從新放進來數據的時候這個 ptr 的值變爲了0x00428e60 ,這是由於當緩衝區中沒有任何數據的時候要將 ptr 這個值進行一下刷新,使其指向緩衝區的基地址也就是0x0042e860這個值!由於下次要從這個位置開始讀取數據!
  在這裏有點須要說明:當咱們從鍵盤輸入字符串的時候須要敲一下回車鍵纔可以將這個字符串送入到緩衝區中,那麼敲入的這個回車鍵(\r)會被轉換爲一個換行符\n,這個換行符\n也會被存儲在緩衝區中而且被當成一個字符來計算!好比咱們在鍵盤上敲下了123456這個字符串,而後敲一下回車鍵(\r)將這個字符串送入了緩衝區中,那麼此時緩衝區中的字節個數是7 ,而不是6。
  緩衝區的刷新就是將指針 ptr 變爲緩衝區的基地址 ,同時 cnt 的值變爲0 ,由於緩衝區刷新后里面是沒有數據的! 

5. 緩衝區操做(設置、清除)

  (1).清除文件緩衝區函數: int fflush(FILE *stream); int flushall(); fflush()函數將清除由stream指向的文件緩衝區裏的內容,經常使用於寫完一些數據後,當即用該函數清除緩衝區,以避免誤操做時,破壞原來的數據。 flushall()將清除全部打開文件所對應的文件緩衝區。

  (2).設置文件緩衝區函數 void setbuf(FILE *stream,char *buf); void setvbuf(FILE *stream,char *buf,int type,unsigned size); 這兩個函數將使得打開文件後,用戶可創建本身的文件緩衝區,而不使用fopen()函數打開文件設定的默認緩衝區。 

  程序輸出有兩種方式:一種是即時處理方式,另外一種是先暫存起來,而後再大塊寫入的方式,前者每每形成較高的系統負擔。所以,c語言實現一般都容許程序員進行實際的寫操做以前控制產生的輸出數據量。這種控制能力通常是經過庫函數setbuf實現的。若是buf是一個大小適當的字符數組,那麼:setbuf(stdout,buf);語句將通知輸入/輸出庫,全部寫入到stdout的輸出都應該使用buf做爲輸出緩衝區,直到buf緩衝區被填滿或者程序員直接調用fflush(譯註:對於由寫操做打開的文件,調用fflush將致使輸出緩衝區的內容被實際地寫入該文件),buf緩衝區中的內容才實際寫入到stdout中。緩衝區的大小由系統頭文件<stdio.h>中的BUFSIZ定義。

  函數setvbuf()用來設定文件流的緩衝區,其原型爲:int setvbuf(FILE * stream, char * buf, int type, unsigned size);【參數】stream爲文件流指針,buf爲緩衝區首地址,type爲緩衝區類型,size爲緩衝區內字節的數量。
  參數類型type說明以下:

    _IOFBF (滿緩衝):當緩衝區爲空時,從流讀入數據。或當緩衝區滿時,向流寫入數據。

    _IOLBF (行緩衝):每次從流中讀入一行數據或向流中寫入—行數據。

    _IONBF (無緩衝):直接從流中讀入數據或直接向流中寫入數據,而沒有緩衝區。

  【返回值】成功返回0,失敗返回非0。 

  setbuf()和setvbuf()函數的實際意義在於:用戶打開一個文件後,能夠創建本身的文件緩衝區,而沒必要使用fopen()函數打開文件時設定的默認緩衝區。這樣就可讓用戶本身來控制緩衝區,包括改變緩衝區大小、定時刷新緩衝區、改變緩衝區類型、刪除流中默認的緩衝區、爲不帶緩衝區的流開闢緩衝區等。

  說明:在打開文件流後,讀取內容以前,調用setvbuf()能夠用來設置文件流的緩衝區。

  文件的隨機讀寫函數

  前面介紹的文件的字符/字符串讀寫,均是進行文件的順序讀寫,即老是從文件的開頭開始進行讀寫。這顯然不能知足咱們的要求,C語言提供了移動文件指針和隨機讀寫的函數,它們是:移動文件指針函數: long ftell(FILE *stream); int rewind(FILE *stream); fseek(FILE *stream,long offset,int origin); 函數ftell()用來獲得文件指針離文件開頭的偏移量。當返回值是-1時表示出錯。rewind()函數用於文件指針移到文件的開頭,當移動成功時,返回0,不然返回一個非0值。fseek()函數用於把文件指針以origin爲起點移動offset個字節,其中origin指出的位置可有如下幾種: origin 數值 表明的具體位置 SEEK_SET 0 文件開頭 SEEK_CUR 1 文件指針當前位置 SEEK_END 2 文件尾 例如: fseek(fp,10L,0); 把文件指針從文件開頭移到第10字節處,因爲offset參數要求是長整型數,故其數後帶L。 fseek(fp,-15L,2); 把文件指針從文件尾向前移動15字節。

6. 文件操做相關函數

1、打開文件操做:FILE *fopen(char *filename, char *mode) 
      filename: 採用絕對或相對路徑的目標文件名 
      mode: 文件的類型和操做要求 
      返回值: 目標文件指針或空指針值NULL(打開異常時)

文件類型 t (text): 文本文件(可省略不寫);   b (banary): 二進制文件

      從文件編碼的方式來看,文件可分爲ASCII碼文件和二進制碼文件兩種。

      ASCII文件也稱爲文本文件,這種文件在磁盤中存放時每一個字符對應一個字節,用於存放對應的ASCII碼。ASCII碼文件可在屏幕上按字符顯示。二進制文件是按二進制的編碼方式來存放文件的。
      二進制文件雖然也可在屏幕上顯示,但其內容沒法讀懂。
      C系統在處理這些文件時,並不區分類型,都當作是字符流,按字節進行處理。輸入輸出字符流的開始和結束只由程序控制而不受物理符號(如回車符)的控制。 所以也把這種文件稱做流式文件。把一個文本文件讀入內存時,要將ASCII碼轉換成二進制碼, 而把文件以文本方式寫入磁盤時,也要把二進制碼轉換成ASCII碼,所以文本文件的讀寫要花費較多的轉換時間。對二進制文件的讀寫不存在這種轉換。

文件操做類型:r (read): 讀【目標文件必須存在,不然報錯】
                      w (write): 寫【目標不存在時自動建立】
                      a (append): 追加【目標文件必須存在,不然報錯】
                      + : 讀和寫

操做類型組合方式

                                                                     操     做     說      明                                                             

"rt"

只讀打開一個文本文件,只容許讀數據

"wt"

只寫打開或創建一個文本文件,只容許寫數據

"at"

追加打開一個文本文件,並在文件末尾寫數據

"rb"

只讀打開一個二進制文件,只容許讀數據

"wb"

只寫打開或創建一個二進制文件,只容許寫數據

"ab"

追加打開一個二進制文件,並在文件末尾寫數據

"rt+"

讀寫打開一個文本文件,容許讀和寫

"wt+"

讀寫打開或創建一個文本文件,容許讀寫

"at+"

讀寫打開一個文本文件,容許讀,或在文件末追加數據

"rb+"

讀寫打開一個二進制文件,容許讀和寫

"wb+"

讀寫打開或創建一個二進制文件,容許讀和寫

"ab+"

讀寫打開一個二進制文件,容許讀,或在文件末追加數據

2) 凡用「r」打開一個文件時,該文件必須已經存在,且只能從該文件讀出。
3) 「w」打開的文件只能向該文件寫入。若打開的文件不存在,則以指定的文件名創建該文件,若打開的文件已經存在,則將該文件刪去,重建一個新文件。
4) 若要向一個已存在的文件追加新的信息,只能用「a」方式打開文件。但此時該文件必須是存在的,不然將會出錯。
5) 在打開一個文件時,若是出錯,fopen將返回一個空指針值NULL。在程序中能夠用這一信息來判別是否完成打開文件的工做,並做相應的處理。

2、關閉文件操做:int fclose(FILE *fp)
      fp 待關閉文件的文件指針。返回值: 0(正常關閉),非0(關閉異常)

3、 讀字符函數: int fgetc(FILE *fp) 
      fp 待讀文件的文件指針。返回值: 讀出字符的ASCII碼或EOF(文件結束時)

      fgetc函數的功能是從指定的文件中讀一個字符。在文件內部有一個位置指針。用來指向文件的當前讀寫字節。在文件打開時,該指針老是指向文件的第一個字節。使用fgetc 函數後, 該位置指針將向後移動一個字節。文件結束時,該指針指向EOF, 所以可連續屢次使用fgetc函數,讀取多個字符直至遇到EOF爲止。 應注意文件指針和文件內部的位置指針不是一回事。文件指針是指向整個文件的,須在程序中定義說明,只要不從新賦值,文件指針的值是不變的。文件內部的位置指針用以指示文件內部的當前讀寫位置,每讀寫一次,該指針均向後移動,它不需在程序中定義說明,而是由系統自動設置的。

  對於fgetc函數的使用有如下幾點說明:1) fgetc函數調用中,讀取的文件必須是以讀或讀寫方式打開的。2) 讀取字符的結果也能夠不向字符變量賦值, 例如: fgetc(fp); 可是讀出的字符不能保存。3) 在文件內部有一個位置指針。用來指向文件的當前讀寫字節。在文件打開時,該指針老是指向文件的第一個字節。使用fgetc 函數後,該位置指針將向後移動一個字節。所以可連續屢次使用fgetc函數,讀取多個字符。

4、 寫字符函數: int fputc(int ch, file *fp)
      ch 待寫入文件的字符的ASCII。fp 待寫文件的文件指針。返回值: 如寫入成功則返回寫入的字符, 不然返回EOF

  putc函數的使用也要說明幾點:1) 被寫入的文件能夠用寫、讀寫、追加方式打開,用寫或讀寫方式寫入字符從文件首開始。如需保留原有文件內容,但願寫入的字符被寫入的文件若不存在,則建立該文件。2) 每寫入一個字符,文件內部位置指針向後移動一個字節。3) fputc函數有一個返回值,如寫入成功則返回寫入的字符,不然返回一個EOF。可用此來判斷寫符,寫入一個文件,再把該文件內容讀出顯示在屏幕上。

5、讀字符串函數:char *fgets(char *str, int num, FILE *fp)
      str 保存從文件讀取出來的字符串。num 表示從文件中讀出的字符串不超過 n-1個字符。在讀入的最後一個字符後加上串結束標誌'/0'。fp 待讀文件的文件指針。返回值: 字符數組的首地址或者NULL(當讀到文件末尾或發生錯誤時返回)
      功能描述: 讀字符串函數fgets函數的功能是從指定的文件中讀一個字符串到字符數組中,如:fgets(str,n,fp)的意義是從fp所指的文件中讀出n-1個字符送入字符數組str中。

      fgets函數有兩點說明:1. 在讀出n-1個字符以前,如遇到了換行符或EOF,則讀出結束。2. fgets函數也有返回值,其返回值是字符數組的首地址。

6、寫字符串函數: int fputs(char *str, file *fp)
      str 待寫入文件的字符串。fp 待寫文件的文件指針。返回值: 非負整數(成功),EOF(失敗
      功能描述: fputs函數的功能是向指定的文件寫入一個字符串

7、數據塊讀寫函數: int fwrite(void *buf, int size, int count, FILE *fp) | int fread(void *buf, int size, int count, FILE *fp)
      buf fread函數中,它表示存放輸入數據的首地址。在fwrite函數中,它表示存放輸出數據的首地址。size 表示數據塊的字節。count 表示要讀寫的數據塊塊數。fp表示文件指針 。返回: 已讀取或已寫入的數據塊塊數
8、 格式化讀寫函數 int fscanf(FILE *fp, char *format,…) | int fprintf(FILE *fp, char *format,…) 
      fscanf函數,fprintf函數與前面使用的scanfprintf 函數的功能類似,都是格式化讀寫函數。 二者的區別在於 fscanf 函數和fprintf函數的讀寫對象不是鍵盤和顯示器,而是磁盤文件。

9、文件的隨機讀寫 
      前面介紹的對文件的讀寫方式都是順序讀寫, 即讀寫文件只能從頭開始,順序讀寫各個數據。 但在實際問題中常要求只讀寫文件中某一指定的部分。 爲了解決這個問題可移動文件內部的位置指針到須要讀寫的位置,再進行讀寫,這種讀寫稱爲隨機讀寫。 實現隨機讀寫的關鍵是要按要求移動位置指針,這稱爲文件的定位。文件定位移動文件內部位置指針的函數主要有兩個, 即 rewind 函數和fseek函數。
  rewind函數前面已屢次使用過,其調用形式爲: rewind(文件指針); 它的功能是把文件內部的位置指針移到文件首。 下面主要介紹fseek函數。
      fseek函數用來移動文件內部位置指針,其調用形式爲: fseek(文件指針,位移量,起始點); 其中:文件指針指向被移動的文件。 位移量表示移動的字節數,要求位移量是long型數據,以便在文件長度大於64KB 時不會出錯。當用常量表示位移量時,要求加後綴「L」起始點表示從何處開始計算位移量,規定的起始點有三種:文件首,當前位置和文件尾。其表示方法以下:

起始點    表示符號    數字表示
──────────────────────────
文件首    SEEK—SET    0
當前位置   SEEK—CUR     1
文件末尾   SEEK—END     2

10、文件檢測函數

      C語言中經常使用的文件檢測函數有如下幾個。1、文件結束檢測函數feof函數調用格式: feof(文件指針) 功能:判斷文件是否處於文件結束位置,如文件結束,則返回值爲1,不然爲0。2、讀寫文件出錯檢測函數ferror函數調用格式: ferror(文件指針) 功能:檢查文件在用各類輸入輸出函數進行讀寫時是否出錯。 如ferror返回值爲0表示未出錯,不然表示有錯。3、文件出錯標誌和文件結束標誌置0函數clearerr函數調用格式: clearerr(文件指針); 功能:本函數用於清除出錯標誌和文件結束標誌,使它們爲0值。

相關文章
相關標籤/搜索