C輸入輸出與檔案

發表於2015-12-26

一、終端I/O

1.單字元I/O:getchar(),putchar()

(1)單字元輸入(get character):   

   【 int getchar();】

  • 返回值為輸入的字元(ASCII)。可以接受任何字元,包括非列印字元。當一次鍵入多個字元時按下Enter鍵後getchar開始逐個讀取所有字元(包括回車符)。
  • 在某些編譯環境下,因為scanf()函式不讀取回車符且將其留在輸入佇列中,下次呼叫scanf()時會由於先讀取到回車符而在讀取資料前過早結束輸入,所以常在scanf後用【getchar();】讀取並丟棄這個回車符。

(2)單字元輸出(put character):

   【int putchar(int);】

  • 返回值為輸出的字元,引數為要輸出的字元(ASCII)。是在stdio.h中定義的含參巨集。

2.格式化輸入輸出

 (1)格式化輸出(print format):

   【 int printf(格式控制,輸出表列);】

  • 格式控制是一個字串常量,由普通(轉義)字元,轉換說明符,轉換修飾符組成。普通(轉義)字元直接輸出(控制游標),轉換說明符會按順序替換為輸出表列中的量。常用的格式說明符有:

   <1> %d %ld 整型或長整型

   <2> %c     字元型,引數可以為對應ASCII碼或單引號內的字元

   <3> %f %lf 浮點數,double型常用%lf。預設6位小數,自動進行四捨五入

   <4> %s     字串型,不輸入輸出'\0'

   <5> %e     用e計數法(科學計數法)輸出浮點數

   <6> %x     輸出16進位制數,%#x輸出0x格式

   <7> %o     無符號8進位制數

   <8> 浮點數%m.nf,m是說明總寬度,正數代表左對齊負數代表右對齊。預設用空格補位,如果m的最高位為0則用0補位。 

C通過變數型別來將變數值存在堆疊(stack)中,但printf函式在讀取時依據轉換說明符。如果較長的資料使用較短的說明符只會讀取一部分(轉換為十進位制後可能變成古怪的數字),較短的資料使用較長的說明符會讀取下一記憶體單元中的字元而輸出不可預知的結果。

printf函式成功輸出會返回輸出的字元數(包括列印字元和非列印字元),失敗返回一個代表失敗型別的負值。

在指定輸出位數時可以用*代替數字,但在輸出表列的對應位置要有一個對應的int值

 

printf("%.*f",2,2.333);

(2)格式輸入(scan format)

   【int scanf(格式控制,地址表列);】

  • 格式控制與printf函式相似,%與格式字元中間的數值代表讀取的位數,%.ns中的n可以限定最大輸入長度。地址表列代表讀取資料儲存的地址,可以是&+變數名,指標。當讀取字串時直接寫字元陣列名不加&,因為它本身就是一個地址。
  • scanf()中除s外[…]和[^…]都可以用於字串輸入。[…]只讀取[…]中出現的字元,讀取到未包含的字元時將停止讀取(可以用來讓scanf()也讀取空格)。[^…]正好相反,只讀取其中不包含的字元,讀取到其中包含的字元時停止讀取。

scanf輸入機制

  • scanf函式按照格式控制逐一讀取輸入字元,當發現與格式控制匹配的輸入時就將其儲存到指定地址中,直到讀取到指定字元數或第一個不符合格式控制的字元。但是scanf會將第一個不符合格式控制的字元(包括為確認輸入而輸入的回車符)留在輸入佇列(輸入緩衝區)中,下次讀取將從這個被捨棄的字元開始。
  • 在多次呼叫scanf時要將上次呼叫時留在輸入佇列中的回車符取出(用getchar()函式),否則scanf會因為這個回車而認為本次輸入了空字串(回車是確認輸入的標誌)而結束讀取。

scanf函式返回值

  • scanf返回它成功讀取的專案數,當它未能成功讀取時返回0。當讀取到檔案結尾時(end of file)返回EOF(EOF是在stdio.h中定義的常量,一般為-1)。scanf的返回值可用來輸入個數未知的資料或檢測處理異常輸入。示例:【while (scanf("%d", &a) != EOF)】或 【while (scanf("%d", &a) == 1)】。因為EOF通常定義為-1被認為是真,所以不能採用 while(scanf(…));。

scanf函式輸入字串

  • scanf函式讀取字串由兩種方法決定結束讀取。都是從第一個非空白字元開始,讀入指定字元數或讀到(但不包括)下一個空白字元時結束。
  • *在%和格式字元中間表示滯後賦值,scanf將跳過這些格式說明直接讀取後面的值。

3.字串輸入或輸出

(1)字串輸入(get string):

    【char *gets(char *);】

  • gets()函式讀取字串以指標引數值為首地址建立字元陣列。 gets()返回的地址與傳遞給它的是同一個地址,也就是字元陣列的首地址。當讀取失敗或讀到檔案結尾時將返回一個空指標(NULL)。gets()將讀取換行符前所有字元並在最後加一個空字元('\0'),把字串首地址交給呼叫它的程式。

與scanf()比較:

  • gets()函式將讀取換行符並把它丟棄,scanf()不讀取換行符,並把它留在輸入佇列中。gets()回讀取空格等字元並儲存到字串中,scanf()遇到空格會停止讀取。
  • gets()函式不能指定輸入字元數,多出的字元將會溢位到相鄰記憶體區。scanf()語句可以指定輸入字元數。

(2)字串輸出(put string):

   【int puts(const char *str);】

  • 向終端輸出字串,並自動輸出換行符返回字元數。

二、檔案I/O

  • 檔案:儲存在外部介質上的資料集合。  在UNIX(c的發源地)中所有裝置都是當做檔案統一處理的(一切都是檔案)。
  • 流:以規定順序被讀取一次的資料序列,可以理解為在輸入輸出過程中產生的一個資料序列。
  • 檔案標識:由檔案路徑,主檔名,檔案字尾組成。
  • 檔案緩衝區:記憶體中的一片區域,對檔案的I/O資料流必須先裝滿緩衝區再送到程式資料區(程式變數)或外存。
  • 檔案資訊區:是一個FILE型別的結構體變數。FILE型別在stdio.h檔案中定義用來存放資料緩衝區位置,緩衝區狀態,檔案狀態等檔案資訊。
  • 檔案指標:指向檔案資訊區的指標。標準檔案指標:stdin 從終端(鍵盤)讀取,stdout 向終端(顯示器)輸出,stderr 向終端(顯示器)傳送錯誤資訊。它們都是在stdio.h中的定義的FILE指標(FILE *),可以作為標準/O函式的引數。
  • 檔案指標-->檔案資訊區-->檔案緩衝區<-(作業系統)->檔案

1.檔案的開啟關閉與重定向

  • 檔案的開啟和關閉是與檔案關聯的檔案資訊區和資料緩衝區建立與撤銷的過程。無說明的函式都在stdio.h標頭檔案中。

(1)開啟檔案(file open):

  【(FILE *) fopen(檔名,模式字串);】

  • 模式字串:

  1)"r" 只讀 "w"只寫 "a" 追加

  2)"b" 二進位制檔案

  3)"+" 可讀寫

  • 在同一個字串中將三部分按順序寫出。

  1)必須寫出,當目標檔案不存在時,"r","a"出錯,"w"將建立一個新檔案。

  2)可不寫,預設時開啟文字檔案。

  3)可不寫,預設時只讀只寫。

  • 關於r、a、w

  1)"r+"在檔案頭開始讀寫但不會清除檔案內容(類似改寫模式)。                 

    2)"a+"為從檔案尾進行讀寫,並自動移動EOF(a模式下不會自動移動EOF,可能會在某些環境下無法讀取追加的內容故推薦a+)。        

    3)"w+"清空檔案後重寫。

  • 函式返回值為指向檔案的指標(FILE *),可用於呼叫檔案。當開啟失敗時返回NULL。

(2)關閉檔案(file close):

  【int fclose(檔案指標);】

  • 撤銷檔案資訊區,資料緩衝區,並使檔案指標不再指向檔案。關閉成功返回0否則返回EOF。
  • 應該先把緩衝區中的資料輸出到檔案,再撤銷緩衝區。

關閉全部檔案:

  【int fcloseall(void);】

(3)重定向(free open??):

  【FILE *freopen(const char* new,const char *type, FILE *stream);】

其中:

  new:新流的名稱

  type:開啟方式(同fileopen函式)

  stream:原有流。

  成功返回新流的指標,失敗返回NULL。

  重定向函式向原有流的讀寫將被向新流的讀寫替代,可以在不改變其它程式碼的情況下使得程式的預設讀寫物件改變常用於將stdin(stdout, stderr)替換為檔案使得scanf或printf等函式實現向檔案讀寫。

 

  若要恢復標準流,可以重新開啟標準控制檯裝置檔案,只是這個裝置檔案的名字是與作業系統相關的。

DOS/Windows:

  【freopen("CON", "r", stdin);】

Linux:

  【freopen("/dev/console", "r", stdin);】

 

(4)重新整理緩衝區(file flush):

  【int fflush(FILE *fp);】

  • 將緩衝區所有未寫的資料傳送到指定檔案中,即重新整理緩衝區。如果引數為空指標則清空緩衝區。

重新整理所有緩衝區:

  【int fflushall(void);】

(5)退出(exit):

  【void exit(int);】

  • 關閉所有開啟的檔案並結束程式,相當於在最初呼叫(即由作業系統命令呼叫的main而非其他函式呼叫的main)main函式中return語句。包含在stdlib.h標頭檔案中。

2.檔案的順序讀寫

(1)從檔案輸入字元(file get character):

  【int fgetc(FILE *fp);】

  • 從檔案中讀取一個字元,並返回它的ASCII碼。

(2)向檔案輸出字元(file put character):

  【int fputc(int ch,FILE *fp)】

向檔案寫字元ch。

(3)從檔案輸入字串(file get string):

  【char *fgets(char *,int,FILE *fp);】

  • 從檔案中讀字串。引數一為字串地址;引數2為允許的最大字元數,如果值為n則讀取n-1個字元,並自動新增一個空字元。引數3為讀取檔案指標,從鍵盤讀取資料時可以使用stdin(standard input)作為該引數,這個識別符號在stdio.h中定義。返回值與gets()函式相同。fgets()本來是為處理檔案I/O而設計也可以用於從終端輸入。fgets()讀取到換行符會將其存放到字串中而不丟棄。

(4)向檔案輸出字串(file put string):

  【int fputs(const *char, FILE *fp)】

  • 向檔案中寫字串。引數1為要輸出的字串常量,引數2為要寫到的檔案。進行顯示時可以使用stdout(standard output)作為引數,這個識別符號在stdio.h中定義。函式返回值為成功輸出的字元數。
  • 面向檔案的字串輸入輸出語句不會自動增刪換行符。

(5)向檔案格式化輸出(file print format):

  【int fprintf(檔案指標,格式字串,輸出表列);】

  • 向檔案格式化輸出。

(6)從檔案格式化輸入(file scanf format):

  【int fscanf(檔案指標,格式字串,地址表列);】

  • 這些檔案讀寫函式讀到檔案結尾時同樣返回EOF,當輸入個數不確定的資料和處理異常輸入時,可以參照scanf函式的用法。
  • 用二進位制方式向檔案讀寫一段資料:
  • 在程式中不僅需要一次輸入輸出一個資料且常常需要一次輸入輸出一組資料(如陣列或結構體),C允許直接將一段記憶體複製到檔案中或從檔案複製到記憶體。

(7)從檔案讀取資料塊(file read)

  【size_t fread(void *buffer,size_t size, size_t count, FILE *fp);】

(8)向檔案輸出資料塊(file write):

  【size_t fwrite(const void *buffer,size_t size, size_t count, FILE *fp);】

其中:

  buffer:向檔案寫出(從檔案讀入)的記憶體區域首地址。

  size:要讀寫的每個資料項的位元組數。

  count:要讀寫的資料項的個數。

  fp:檔案指標。

  在開啟檔案時以二進位制檔案開啟,就可以讀寫任何型別的資料。

3.檔案的隨機讀寫

  • 檔案的順序讀寫是按照資料在檔案中物理位置次序進行讀寫,先讀取前面的資料再讀取後面的資料。而檔案的隨機讀寫可以訪問任意位置的資料,顯然這種方法比順序讀寫高效得多。(類比陣列與連結串列的讀寫)
  • 為了對檔案讀寫進行控制,系統對每個檔案設定了檔案讀寫位置標記(簡稱檔案位置標記或檔案標記),用來指示要讀寫的下一個字元的位置。開啟檔案時檔案標記指向檔案頭,完成讀寫後指向下一個字元處(類似記事本中的游標)。

改變檔案讀寫標記:fseek()和rewind()

(1)讀寫標記定位(file seek):

  【int fseek(FILE *filename, long int offset, int from);】

其中:

  filename:操作的檔案物件

  offset:偏移量(長整型,要在數字末尾加一個L),正值             表示向檔案結尾移動,負值表示向檔案開頭移動。

  from:移動的起始點,C中定義:

       0 - SEEK_SET  檔案開頭

       1 - SEEK_CUR 當前位置

       2 - SEEK_END 檔案結尾

(2)讀寫標記重置(rewind):

  【void rewind(FILE *filename);】

  • 使檔案標記指向檔案頭部。當以讀寫模式開啟字串時需要特別注意標記的位置。

(3)判斷檔案標記位置(file tell):

  【long int ftell(FILE *fp);】

(4)返回檔案位置標記的位置(end of file):

  【int feof(FILE *fp);】

  • 當檔案位置標記位於檔案結尾時返回一個非0值,否則返回0。

(5)檔案讀寫出錯檢測(file error):

  【ferror(fp);】

  • 返回0表示未出錯,返回非0值表示出錯。
  • 當在資料長度未知時需要邊讀取邊判斷是否到達EOF,由於有時邊讀取邊處理,當輸入函式讀取到EOF時遍停止讀取不更新變數,可能出現最後一組資料被處理並輸出了兩次的情況。這時不僅需要使用feof來控制迴圈次數,也要同時在讀取後使用feof判斷是否提前跳出迴圈。特別地,fread函式讀取到EOF時會發生錯誤此時需要用ferror來進行判斷。

(6)清除錯誤標記(clear error):

  【void clearerr(FILE *fp);】

  • 使檔案錯誤標誌和檔案結尾標誌置為0。只要出現檔案讀寫錯誤標誌它就一直存在,直到呼叫clearerr,rewind函式,或下一次進行輸入輸出。

 

相關文章