文件有不一樣的類型,在程序設計中,主要用到兩種文件:數組
(1) 程序文件。包括源程序文件(後綴爲.c)、目標文件(後綴爲.obj)、可執行文件(後綴爲.exe)等。這種文件的內容是程序代碼。函數 |
(2) 數據文件。文件的內容不是程序,而是供程序運行時讀寫的數據,如在程序運行過程當中輸出到磁盤(或其餘外部設備)的數據,或在程序運行過程當中供讀入的數據。如一批學生的成績數據、貨物交易的數據等。this |
說明:spa 爲了簡化用戶對輸入輸出設備的操做,使用戶沒必要去區分各類輸入輸出設備之間的區別,操做系統把各類設備都統一做爲文件來處理。從操做系統的角度看,每個與主機相連的輸入輸出設備都看做一個文件。例如,終端鍵盤是輸入文件,顯示屏和打印機是輸出文件。操作系統 |
文件(file)通常指存儲在外部介質上數據的集合。操做系統是以文件爲單位對數據進行管理的。設計
輸入輸出是數據傳送的過程,數據如流水同樣從一處流向另外一處,所以常將輸入輸出形象地稱爲流(stream),即數據流。指針
流表示了信息從源到目的端的流動。在輸入操做時,數據從文件流向計算機內存,在輸出操做時,數據從計算機流向文件(如打印機、磁盤文件)。對象
C的數據文件由一連串的字符(或字節)組成,而不考慮行的界限,兩行數據間不會自動加分隔符,對文件的存取是以字符(字節)爲單位的。輸入輸出數據流的開始和結束僅受程序控制而不受物理符號(如回車換行符)控制,這就增長了處理的靈活性。這種文件稱爲流式文件。blog
一個文件要有一個惟一的文件標識,以便用戶識別和引用。排序
文件標識包括3部分: (1)文件路徑; (2)文件名主幹; (3)文件後綴。
(1) 文件路徑表示文件在外部存儲設備中的位置 |
(2) 文件名主幹的命名規則遵循標識符的命名規則 |
(3) 文件後綴用來表示文件的性質 |
常見的後綴有:mp3 mp4 docx pptx jpg png 等
爲方便起見,文件標識常被稱爲文件名,但應瞭解此時所稱的文件名,實際上包括以上3部份內容,而不只是文件名主幹。
根據數據的組織形式,數據文件可分爲ASCII文件和二進制文件。
說明: 數據在內存中是以二進制形式存儲的,若是不加轉換地輸出到外存,就是二進制文件,能夠認爲它就是存儲在內存的數據的映像,因此也稱之爲映像文件(image file)。若是要求在外存上以ASCII代碼形式存儲,則須要在存儲前進行轉換。ASCII文件又稱文本文件(text file),每個字節存放一個字符的ASCII代碼。 |
既能夠用ASCII形式存儲,也能夠用二進制形式存儲。
說明: 用ASCII碼形式輸出時字節與字符一 一對應,一個字節表明一個字符,於是便於對字符進行逐個處理,也便於輸出字符。但通常佔存儲空間較多,並且要花費轉換時間(二進制形式與ASCII碼間的轉換)。用二進制形式輸出數值,能夠節省外存空間和轉換時間,把內存中的存儲單元中的內容原封不動地輸出到磁盤(或其餘外部介質)上,此時每個字節並不必定表明一個字符。 |
簡單來講,就是若是使用ASCII碼須要進行轉換,而使用二進制則不須要轉換。
ANSI C標準採用「緩衝文件系統」處理數據文件,所謂緩衝文件系統是指系統自動地在內存區爲程序中每個正在使用的文件開闢一個文件緩衝區。
解釋: 從內存向磁盤輸出數據必須先送到內存中的緩衝區,裝滿緩衝區後才一塊兒送到磁盤去。若是從磁盤向計算機讀入數據,則一次從磁盤文件將一批數據輸入到內存緩衝區(充滿緩衝區),而後再從緩衝區逐個地將數據送到程序數據區(給程序變量)。這樣作是爲了節省存取時間,提升效率,緩衝區的大小由各個具體的C編譯系統肯定。 |
說明: 每個文件在內存中只有一個緩衝區,在向文件輸出數據時,它就做爲輸出緩衝區,在從文件輸入數據時,它就做爲輸入緩衝區。 |
簡單來講,就是程序運行爲了節省存取時間,提升效率,則咱們就須要使用到緩衝區。
緩衝文件系統中,關鍵的概念是「文件類型指針」,簡稱「文件指針」。
說明: 每一個被使用的文件都在內存中開闢一個相應的文件信息區,用來存放文件的有關信息(如文件的名字、文件狀態及文件當前位置等)。這些信息是保存在一個結構體變量中的。該結構體類型是由系統聲明的,取名爲FILE。 |
一種C編譯環境提供的stdio.h頭文件中有如下的文件類型聲明:
typedef struct { short level; //緩衝區「滿」或「空」的程度 unsigned flags; //文件狀態標誌 char fd; //文件描述符 unsigned char hold; //如緩衝區無內容不讀取字符 short bsize; //緩衝區的大小 unsigned char*buffer; //數據緩衝區的位置 unsigned char*curp; //文件位置標記指針當前的指向 unsigned istemp; //臨時文件指示器 short token; //用於有效性檢查 }FILE; |
FILE *fp; //定義一個指向FILE類型數據的指針變量 |
解釋: 可使fp指向某一個文件的文件信息區(是一個結構體變量),經過該文件信息區中的信息就可以訪問該文件。也就是說,經過文件指針變量可以找到與它關聯的文件。若是有n個文件,應設n個指針變量,分別指向n個FILE類型變量,以實現對n個文件的訪問。爲方便起見,一般將這種指向文件信息區的指針變量簡稱爲指向文件的指針變量。 |
注:指向文件的指針變量並非指向外部介質上的數據文件的開頭,而是指向內存中的文件信息區的開頭。
對文件讀寫以前應該「打開(Open)」該文件,在使用結束以後應「關閉(Close)」該文件。
說明: 「打開」是指爲文件創建相應的信息區(用來存放有關文件的信息)和文件緩衝區(用來暫時存放輸入輸出的數據)。在編寫程序時,在打開文件的同時,通常都指定一個指針變量指向該文件,也就是創建起指針變量與文件之間的聯繫,這樣,就能夠經過該指針變量對文件進行讀寫了。 |
「關閉」是指撤銷文件信息區和文件緩衝區,使文件指針變量再也不指向該文件,顯然就沒法進行對文件的讀寫了。 |
ANSI C規定了用標準輸入輸出函數fopen來實現打開文件。
Open函數的調用方式爲
fopen(文件名,使用文件方式); |
[例] fopen使用示例
FILE*fp; //定義一個指向文件的指針變量fp fp=fopen(″a1″,″r″); //將fopen函數的返回值賦給指針變量fp,表示以「讀入」方式打開名字爲a1的文件 |
在打開一個文件時,通知編譯系統如下3個信息: ① 須要打開文件的名字,也就是準備訪問的文件的名字 ② 使用文件的方式(「讀」仍是「寫」等) ③ 讓哪個指針變量指向被打開的文件 |
使用文件方式有以下形式:
說明:
(1) 用「r」方式打開的文件只能用於向計算機輸入而不能用做向該文件輸出數據,並且該文件應該已經存在,並存有數據,這樣程序才能從文件中讀數據。不能用「r」方式打開一個並不存在的文件,不然出錯。 |
(2) 用「w」方式打開的文件只能用於向該文件寫數據(即輸出文件),而不能用來向計算機輸入。若是原來不存在該文件,則在打開文件前新創建一個以指定的名字命名的文件。若是原來已存在一個以該文件名命名的文件,則在打開文件前先將該文件刪去,而後從新創建一個新文件。 |
(3) 若是但願向文件末尾添加新的數據(不但願刪除原有數據),則應該用「a」方式打開。但此時應保證該文件已存在;不然將獲得出錯信息。在每一個數據文件中自動設置了一個隱式的「文件讀寫位置標記」,它指向的位置就是當前進行讀寫的位置。若是「文件讀寫位置標記」在文件開頭,則下一次的讀寫就是文件開頭的數據。而後「文件讀寫位置標記」自動移到下一個讀寫位置,以便讀寫下一個數據。以添加方式打開文件時,文件讀寫位置標記移到文件末尾。 |
(4) 用「r+」「w+」「a+」方式打開的文件既可用來輸入數據,也可用來輸出數據。 |
(5) 若是不能實現「打開」的任務,fopen函數將會帶回一個空指針值NULL。 |
(6) C標準建議用表10.1列出的文件使用方式打開文本文件或二進制文件,但目前使用的有些C編譯系統可能不徹底提供全部這些功能,須要注意所用系統的規定。 |
(8) 若是用「wb」的文件使用方式,並不意味着在文件輸出時把內存中按ASCII形式保存的數據自動轉換成二進制形式存儲。輸出的數據形式是由程序中採用什麼讀寫語句決定的。例如,用fscanf和fprintf函數是按ASCII方式進行輸入輸出,而fread和fwrite函數是按二進制進行輸入輸出。 |
(9) 程序中可使用3個標準的流文件——標準輸入流、標準輸出流和標準出錯輸出流。系統已對這3個文件指定了與終端的對應關係。標準輸入流是從終端的輸入,標準輸出流是向終端的輸出,標準出錯輸出流是當程序出錯時將出錯信息發送到終端。程序開始運行時系統自動打開這3個標準流文件。 |
注意:咱們推薦使用如下方式打開文件
//打開一個文件的經常使用方法 if ((fp=fopen(″file1″,″r″))==NULL) { printf(″cannot open this file\n″); exit(0); } |
在使用完一個文件後應該關閉它,以防止它再被誤用。
解釋: 「關閉」就是撤銷文件信息區和文件緩衝區,使文件指針變量再也不指向該文件,也就是文件指針變量與文件「脫鉤」,此後不能再經過該指針對原來與其相聯繫的文件進行讀寫操做,除非再次打開,使該指針變量從新指向該文件。 |
若是不關閉文件就結束程序運行將會丟失數據。
說明: 在向文件寫數據時,是先將數據輸出到緩衝區,待緩衝區充滿後才正式輸出給文件。若是當數據未充滿緩衝區時程序結束運行,就有可能使緩衝區中的數據丟失。用fclose函數關閉文件時,先把緩衝區中的數據輸出到磁盤文件,而後才撤銷文件信息區。有的編譯系統在程序結朿前會自動先將緩衝區中的數據寫到文件,從而避免了這個問題,但仍是應當養成在程序終止以前關閉全部文件的習慣。 |
關閉文件用fclose函數。fclose函數調用的通常形式爲:
fclose(文件指針);
如:fclose(fp);
注:fclose函數也帶回一個值,當成功地執行了關閉操做,則返回值爲0;不然返回EOF(-1)。
讀寫一個字符的函數:
函數名 |
調用形式 |
功能 |
返回值 |
fgetc |
fgetc(fp) |
從fp指向的文件讀入一個字符 |
讀成功,帶回所讀的字符,失敗則返回文件結束標誌EOF(即-1) |
fputc |
fputc(ch,fp) |
把字符ch寫到文件指針變量fp所指向的文件中 |
輸出成功,返回值就是輸出的字符;輸出失敗,則返回EOF(即-1) |
說明:
fgetc的第1個字母f表明文件(file),中間的get表示「獲取」,最後一個字母c表示字符(character),fgetc的含義很清楚: 從文件讀取一個字符。fputc也相似。 |
[例] 從鍵盤輸入一些字符,並逐個把它們送到磁盤上去,直到用戶輸入一個「#」爲止。
#include <stdio.h> #include <stdlib.h> int main() { FILE *fp; //定義文件指針fp char ch,filename[10]; printf("請輸入所用的文件名: "); scanf("%s",filename); //輸入文件名 getchar(); //用來消化最後輸入的回車符 if((fp=fopen(filename,"w"))==NULL) //打開輸出文件並使fp指向此文件 { printf("cannot open file\n"); //若是打開出錯就輸出「打不開」 exit(0); //終止程序 } printf("請輸入一個準備存儲到磁盤的字符串(以#結束): "); ch=getchar(); //接收從鍵盤輸入的第一個字符 while(ch!='#') //當輸入′#′時結束循環 { fputc(ch,fp); //向磁盤文件輸出一個字符 putchar(ch); //將輸出的字符顯示在屏幕上 ch=getchar(); //再接收從鍵盤輸入的一個字符 } fclose(fp); //關閉文件 putchar(10); //向屏幕輸出一個換行符 return 0; } |
說明: 用來存儲數據的文件名能夠在fopen函數中直接寫成字符串常量形式 ,也能夠在程序運行時由用戶臨時指定。
用fopen函數打開一個「只寫」的文件(「w」表示只能寫入不能從中讀數據),若成功,函數返回該文件所創建的信息區的起始地址給文件指針變量fp。若失敗,則顯示「沒法打開此文件」,用exit函數終止程序運行,此函數在stdlib.h頭文件中。
用getchar函數接收用戶從鍵盤輸入的字符。注意每次只能接收一個字符。 |
運行結果:
[例] 將一個磁盤文件中的信息複製到另外一個磁盤文件中。今要求將上例創建的file1.dat文件中的內容複製到另外一個磁盤文件file2.dat中。
#include <stdio.h> #include <stdlib.h> int main() { FILE *in,*out; //定義指向FILE類型文件的指針變量 char ch,infile[10],outfile[10]; //定義兩個字符數組,分別存放兩個數據文件名 printf("輸入讀入文件的名字:"); scanf("%s",infile); //輸入一個輸入文件的名字 printf("輸入輸出文件的名字:"); scanf("%s",outfile); //輸入一個輸出文件的名字 if((in=fopen(infile,"r"))==NULL) //打開輸入文件 { printf("沒法打開此文件\n"); exit(0); } if((out=fopen(outfile,"w"))==NULL) //打開輸出文件 { printf("沒法打開此文件\n"); exit(0); } ch=fgetc(in); //從輸入文件讀入一個字符,賦給變量ch while(!feof(in)) //若是未遇到輸入文件的結束標誌 { fputc(ch,out); //將ch寫到輸出文件 putchar(ch); //將ch顯示到屏幕上 ch=fgetc(in); //再從輸入文件讀入一個字符,賦給變量ch } putchar(10); //顯示徹底部字符後換行 fclose(in); //關閉輸入文件 fclose(out); //關閉輸出文件 return 0; } |
說明: 在訪問磁盤文件時,是逐個字符(字節)進行的,爲了知道當前訪問到第幾個字節,系統用「文件讀寫位置標記」來表示當前所訪問的位置。開始時「文件讀寫位置標記」指向第1個字節,每訪問完一個字節後,當前讀寫位置就指向下一個字節,即當前讀寫位置自動後移。
爲了知道對文件的讀寫是否完成,只須看文件讀寫位置是否移到文件的末尾。 |
運行結果:
讀寫一個字符串的函數:
函數名 |
調用形式 |
功能 |
返回值 |
fgets |
fgets(str,n,fp) |
從fp指向的文件讀入一個長度爲(n-1)的字符串,存放到字符數組str中 |
讀成功,返回地址str,失敗則返回NULL |
fputs |
fputs(str,fp) |
把str所指向的字符串寫到文件指針變量fp所指向的文件中 |
輸出成功,返回0;不然返回非0值 |
說明:
fgets中最後一個字母s表示字符串(string)。見名知義,fgets的含義是: 從文件讀取一個字符串。 |
fgets函數的函數原型爲:
char *fgets(char*str, int n, FILE*fp); |
其做用是從文件讀入一個字符串。調用時能夠寫成下面的形式:
fgets(str,n,fp); |
解釋: 其中,n是要求獲得的字符個數,但實際上只從fp所指向的文件中讀入n-1個字符,而後在最後加一個′\0′字符,這樣獲得的字符串共有n個字符,把它們放到字符數組str中。若是在讀完n-1個字符以前遇到換行符「\n」或文件結束符EOF,讀入即結束,但將所遇到的換行符「\n」也做爲一個字符讀入。若執行fgets函數成功,則返回值爲str數組首元素的地址,若是一開始就遇到文件尾或讀數據出錯,則返回NULL。 |
fputs函數的函數原型爲:
int fputs (char *str, FILE *fp); |
其做用是將str所指向的字符串輸出到fp所指向的文件中。調用時能夠寫成:
fputs("China",fp); |
解釋: 把字符串″China″輸出到fp指向的文件中。fputs函數中第一個參數能夠是字符串常量、字符數組名或字符型指針。字符串末尾的′\0′不輸出。若輸出成功,函數值爲0;失敗時,函數值爲EOF(即-1)。 |
說明:
fgets和fgets這兩個函數的功能相似於gets和puts函數,只是gets和puts以終端爲讀寫對象,而fgets和fputs函數以指定的文件做爲讀寫對象。 |
[例] 從鍵盤讀入若干個字符串,對它們按字母大小順序排序,而後把排好序的字符串送到磁盤文件中保存
#include <stdio.h> #include <stdlib.h> #include <string.h> int main() { FILE*fp; char str[3][10],temp[10]; //str是用來存放字符串的二維數組,temp是臨時數組 int i,j,k,n=3; printf("Enter strings:\n"); //提示輸入字符串 for(i=0;i<n;i++) gets(str[i]); //輸入字符串 for(i=0;i<n-1;i++) //用選擇法對字符串排序 { k=i; for(j=i+1;j<n;j++) if(strcmp(str[k],str[j])>0) k=j; if(k!=i) { strcpy(temp,str[i]); strcpy(str[i],str[k]); strcpy(str[k],temp);} } if((fp=fopen("D:\\CC\\string.dat","w"))==NULL) //打開磁盤文件 //′\′爲轉義字符的標誌,所以在字符串中要表示′\′用′\\′ { printf("can′t open file!\n"); exit(0); } printf("\nThe new sequence:\n"); for(i=0;i<n;i++) { fputs(str[i],fp);fputs("\n",fp); //向磁盤文件寫一個字符串,而後輸出一個換行符 printf("%s\n",str[i]); //在屏幕上顯示 } return 0; } |
運行結果:
[例] 從鍵盤讀入若干個字符串,對它們按字母大小的順序排序,而後把排好序的字符串送到磁盤文件中保存。(能夠編寫出如下的程序,從文件string.dat中讀回字符串,並在屏幕上顯示)
#include <stdio.h> #include <stdlib.h> int main() { FILE*fp; char str[3][10]; int i=0; if((fp=fopen("D:\\CC\\string.dat","r"))==NULL) //注意文件路徑必須與前相同 { printf("can′t open file!\n"); exit(0); } while(fgets(str[i],10,fp)!=NULL) { printf("%s",str[i]); i++; } fclose(fp); return 0; } |
運行結果:
能夠對文件進行格式化輸入輸出,這時就要用fprintf函數和fscanf函數。
說明: 從函數名能夠看到,它們只是在printf和scanf的前面加了一個字母f。它們的做用與printf函數和scanf函數相仿,都是格式化讀寫函數。只有一點不一樣: fprintf和fscanf函數的讀寫對象不是終端而是文件 |
它們的通常調用方式爲:
fprintf(文件指針, 格式字符串, 輸出表列); |
fscanf(文件指針, 格式字符串, 輸出表列); |
使用方式以下:
fprintf (fp,″%d,%6.2f″,i,f); //將int型變量i和float型變量f的值按%d和%6.2f的格式輸出到fp指向的文件中 |
fscanf (fp,″%d,%f″,&i,&f); //磁盤文件上若是有字符「3,4.5」,則從中讀取整數3送給整型變量i,讀取實數4.5送給float型變量f |
說明:
C語言容許用fread函數從文件中讀一個數據塊,用fwrite函數向文件寫一個數據塊。在讀寫時是以二進制形式進行的。在向磁盤寫數據時,直接將內存中一組數據原封不動、不加轉換地複製到磁盤文件上,在讀入時也是將磁盤文件中若干字節的內容一批讀入內存。 |
通常調用形式爲:
fread(buffer, size, count, fp); |
fwrite(buffer, size, count, fp); |
解釋: buffer:是一個地址。對fread,它是用來存放從文件讀入的數據的存儲區的地址。對fwrite,是要把此地址開始的存儲區中的數據向文件輸出(以上指的是起始地址)
size:要讀寫的字節數
count:要讀寫多少個數據項(每一個數據項長度爲size)
fp:FILE類型指針 |
使用例子以下
float f[10]; fread(f,4,10,fp); //從fp所指向的文件讀入10個4個字節的數據,存儲到數組f中 |
[例] 從鍵盤輸入10個學生的有關數據,而後把它們轉存到磁盤文件上去。
#include <stdio.h> #define SIZE 10 struct Student_type { char name[10]; int num; int age; char addr[15]; }stud[SIZE]; //定義全局結構體數組stud,包含10個學生數據
void save() //定義函數save,向文件輸出SIZE個學生的數據 { FILE *fp; int i; if((fp=fopen("stu.dat","wb"))==NULL) //打開輸出文件stu.dat { printf("cannot open file\n"); return; } for(i=0;i<SIZE;i++) if(fwrite(&stud[i],sizeof(struct Student_type),1,fp)!=1) printf("file write error\n"); fclose(fp); }
int main() { int i; printf("Please enter data of students:\n"); for(i=0;i<SIZE;i++) //輸入SIZE個學生的數據,存放在數組stud中 scanf("%s%d%d%s",stud[i].name,&stud[i].num, &stud[i].age,stud[i].addr); save(); return 0; } |
運行結果:
[例] 從鍵盤輸入10個學生的有關數據,而後把它們轉存到磁盤文件上去。
(爲了驗證在磁盤文件stu.dat中是否已存在此數據,能夠用如下程序從stu.dat文件中讀入數據,而後在屏幕上輸出)
#include <stdio.h> #include <stdlib.h> #define SIZE 10 struct Student_type { char name[10]; int num; int age; char addr[15]; }stud[SIZE];
int main() { int i; FILE *fp; if((fp=fopen("stu.dat","rb"))==NULL) //打開輸入文件stu.dat { printf("cannot open file\n"); exit(0); } for(i=0;i<SIZE;i++) { fread(&stud[i],sizeof(struct Student_type),1,fp); //從fp指向的文件讀入一組數據 printf("%-10s %4d %4d %-15s\n",stud[i].name,stud[i].num,stud[i]. age,stud[i].addr); //在屏幕上輸出這組數據 } fclose(fp); //關閉文件stu_list return 0; } |
運行結果:
[例] 從鍵盤輸入10個學生的有關數據,而後把它們轉存到磁盤文件上去。
(從磁盤文件stu_list中讀二進制數據,並存放在stud數組中)
##include <stdio.h> #define SIZE 10 struct Student_type { char name[10]; int num; int age; char addr[15]; }stud[SIZE]; //定義全局結構體數組stud,包含10個學生數據 void load() { FILE *fp; int i; if((fp=fopen("stu_list","rb"))==NULL) //打開輸入文件stu_list { printf("cannot open infile\n"); return; } for(i=0;i<SIZE;i++) if(fread(&stud[i],sizeof(struct Student_type),1,fp)!=1) //從stu_ list文件中讀數據 { if(feof(fp)) { fclose(fp); return; } printf("file read error\n"); } fclose(fp); } void save() //定義函數save,向文件輸出SIZE個學生的數據 { FILE *fp; int i; if((fp=fopen("stu.dat","wb"))==NULL) //打開輸出文件stu.dat { printf("cannot open file\n"); return; } for(i=0;i<SIZE;i++) if(fwrite(&stud[i],sizeof(struct Student_type),1,fp)!=1) printf("file write error\n"); fclose(fp); } int main() { int i; load(); save(); return 0; } |
總結:
(1) 數據的存儲方式 文本方式: 數據以字符方式(ASCII代碼)存儲到文件中。如整數12,送到文件時佔2個字節,而不是4個字節。以文本方式保存的數據便於閱讀 二進制方式: 數據按在內存的存儲狀態原封不動地複製到文件。如整數12,送到文件時和在內存中同樣佔4個字節 |
(2) 文件的分類 文本文件(ASCII文件): 文件中所有爲ASCII字符 二進制文件: 按二進制方式把在內存中的數據複製到文件的,稱爲二進制文件,即映像文件 |
(3) 文件的打開方式 文本方式: 不帶b的方式,讀寫文件時對換行符進行轉換 二進制方式: 帶b的方式,讀寫文件時對換行符不進行轉換 |
(4) 文件讀寫函數 文本讀寫函數: 用來向文本文件讀寫字符數據的函數(如fgetc,fgets,fputc,fputs,fscanf,fprintf等) 二進制讀寫函數: 用來向二進制文件讀寫二進制數據的函數(如getw,putw,fread,fwrite等) |
說明: 對文件進行順序讀寫比較容易理解,也容易操做,但有時效率不高。而 隨機訪問不是按數據在文件中的物理位置次序進行讀寫,而是能夠對任何位置上的數據進行訪問,顯然這種方法比順序訪問效率高得多。 |
爲了對讀寫進行控制,系統爲每一個文件設置了一個文件讀寫位置標記(簡稱文件位置標記或文件標記),用來指示「接下來要讀寫的下一個字符的位置」。
說明: 通常狀況下,在對字符文件進行順序讀寫時,文件位置標記指向文件開頭,這時若是對文件進行讀/寫的操做,就讀/寫完第1個字符後,文件位置標記順序向後移一個位置,在下一次執行讀/寫操做時,就將位置標記指向的第2個字符進行讀出或寫入。依此類推,直到遇文件尾,,此時文件位置標記在最後一個數據以後。 |
如圖所示:
說明: 對流式文件既能夠進行順序讀寫,也能夠進行隨機讀寫。關鍵在於控制文件的位置標記。若是文件位置標記是按字節位置順序移動的,就是順序讀寫。若是能將文件位置標記按須要移動到任意位置,就能夠實現隨機讀寫。所謂隨機讀寫,是指讀寫完上一個字符(字節)後,並不必定要讀寫其後續的字符(字節),而能夠讀寫文件中任意位置上所須要的字符(字節)。即對文件讀寫數據的順序和數據在文件中的物理順序通常是不一致的。能夠在任何位置寫入數據,在任何位置讀取數據。 |
(1) 用rewind函數使文件位置標記指向文件開頭 rewind函數的做用是使文件位置標記從新返回文件的開頭,此函數沒有返回值。 形式: rewind(文件指針); |
(2) 用fseek函數改變文件位置標記 fseek函數通常用於二進制文件。 形式: fseek(文件類型指針, 位移量, 起始點); 說明: 「起始點」:用0,1或2代替,0表明「文件開始位置」,1爲「當前位置」,2爲「文件末尾位置」 「位移量」:指以「起始點」爲基點,向前移動的字節數(長整型) 使用方式爲: fseek (fp,100L,0); //將文件位置標記向前移到離文件開頭100個字節處 fseek (fp,50L,1); //將文件位置標記向前移到離當前位置50個字節處 fseek (fp,-10L,2); //將文件位置標記從文件末尾處向後退10個字節 |
(3) 用ftell函數測定文件位置標記的當前位置 ftell函數的做用是獲得流式文件中文件位置標記的當前位置,用相對於文件開頭的位移量來表示。若是調用函數時出錯(如不存在fp指向的文件),ftell函數返回值爲-1L。 使用方式: i=ftell(fp); //變量i存放文件當前位置 if(i==-1L) printf(″error\n″); //若是調用函數時出錯,輸出″error″ |
[例] 有一個磁盤文件,內有一些信息,要求第1次將它的內容顯示在屏幕上,第2次把它複製到另外一文件上
#include<stdio.h> int main() { char ch; FILE *fp1,*fp2; fp1=fopen("file1.dat","r"); //打開輸入文件 fp2=fopen("file2.dat","w"); //打開輸出文件 ch=getc(fp1); //從file1.dat文件讀入第一個字符 while(!feof(fp1)) //當未讀取文件尾標誌 { putchar(ch); //在屏幕輸出一個字符 ch=getc(fp1); //再從file1.dat文件讀入一個字符 } putchar(10); //在屏幕執行換行 rewind(fp1); //使文件位置標記返回文件開頭 ch=getc(fp1); //從file1.dat文件讀入第一個字符 while(!feof(fp1)) //當未讀取文件尾標誌 { fputc(ch,fp2); //向file2.dat文件輸出一個字符 ch=fgetc(fp1); //再從file1.dat文件讀入一個字符 } fclose(fp1);fclose(fp2); return 0; } |
運行結果:
[例] 在磁盤文件上存有10個學生的數據。要求將第1,3,5,7,9個學生數據輸入計算機,並在屏幕上顯示出來。
提示:使用rewind和fseek函數
#include<stdio.h> #include <stdlib.h> struct Student_type //學生數據類型 { char name[10]; int num; int age; char addr[15]; }stud[10]; int main() { int i; FILE *fp; if((fp=fopen("stu.dat","rb"))==NULL) //以只讀方式打開二進制文件 { printf("can not open file\n"); exit(0); } for(i=0;i<10;i+=2) { fseek(fp,i*sizeof(struct Student_type),0); //移動文件位置標記 fread(&stud[i],sizeof(struct Student_type),1,fp); //讀一個數據塊到結構體變量 printf("%-10s %4d %4d %-15s\n",stud[i].name,stud[i].num,stud[i].age,stud[i].addr); //在屏幕輸出 } fclose(fp); return 0; } |
運行結果:
C提供一些函數用來檢查輸入輸出函數調用時可能出現的錯誤。
它的通常調用形式爲: ferror(fp); |
說明: 在調用各類輸入輸出函數(如putc,getc,fread,fwrite等)時,若是出現錯誤,除了函數返回值有所反映外,還能夠用ferror函數檢查。
若是ferror返回值爲0(假),表示未出錯;若是返回一個非零值,表示出錯。 |
說明: clearerr的做用是使文件出錯標誌和文件結束標誌置爲0。
假設在調用一個輸入輸出函數時出現錯誤,ferror函數值爲一個非零值。應該當即調用clearerr(fp),使ferror(fp)的值變成0,以便再進行下一次的檢測。
只要出現文件讀寫出錯標誌,它就一直保留,直到對同一文件調用clearerr函數或rewind函數,或任何其餘一個輸入輸出函數。 |
注:對同一個文件每一次調用輸入輸出函數,都會產生一個新的ferror函數值,所以,應當在調用一個輸入輸出函數後當即檢查ferror函數的值,不然信息會丟失。在執行fopen函數時,ferror函數的初始值自動置爲0。