C語言中文網 目錄
首頁 > C語言專題 > 文件操作 閱讀:1,716

C語言文件操作完全攻略

數據的輸入和輸出幾乎伴隨著每個 C 語言程序,所謂輸入就是從“源端”獲取數據,所謂輸出可以理解為向“終端”寫入數據。這里的源端可以是鍵盤、鼠標、硬盤、光盤、掃描儀等輸入設備,終端可以是顯示器、硬盤、打印機等輸出設備。在 C 語言中,把這些輸入和輸出設備也看作“文件”。

文件及其分類

計算機上的各種資源都是由操作系統管理和控制的,操作系統中的文件系統,是專門負責將外部存儲設備中的信息組織方式進行統一管理規劃,以便為程序訪問數據提供統一的方式。

文件是操作系統管理數據的基本單位,文件一般是指存儲在外部存儲介質上的有名字的一系列相關數據的有序集合。它是程序對數據進行讀寫操作的基本對象。在 C 語言中,把輸入和輸出設備都看作文件。

文件一般包括三要素:文件路徑、文件名、后綴。

由于在 C 語言中 '\' 一般是轉義字符的起始標志,故在路徑中需要用兩個 '\' 表示路徑中目錄層次的間隔,也可以使用 '/' 作為路徑中的分隔符。

例如,"E:\\ch10.doc"或者"E:/ch10.doc",表示文件 ch10.doc 保存在 E 盤根目錄下。"f1.txt" 表示當前目錄下的文件 f1.txt。

文件路徑:可以顯式指出其絕對路徑,如上面的”E:\\”或者”E:/”等;如果沒有顯式指出其路徑,默認為當前路徑。

C 語言不僅支持對當前目錄和根目錄文件的操作,也支持對多級目錄文件的操作,例如:

D:\\C_WorkSpace\\Chapter_10\\file_1.txt

或者

D:/C_WorkSpace/Chapter_10/file_1.txt

中的 file_1.txt 均是 C 語言可操作的多級目錄文件。

文件名:標識文件名字的合法標識符,如 ch10、file_1 等都是合法的文件名。

后綴:一般用于標明文件的類型,使用方式為:文件名.后綴,即文件名與后綴之間用 '.' 隔開。常見的后綴類型有:doc、txt、dat、c、cpp、obj、exe、bmp、jpg 等。

C 語言中的輸入和輸出都是和文件相關的,即程序從文件中輸入(讀?。祿?,程序向文件中輸出(寫入)數據。

文件按其邏輯結構可分為:記錄文件流式文件。而記錄文件又可分為:順序文件、索引文件、索引順序文件散列文件等。

流式文件是以字節為單位,對流式文件的訪問一般采用窮舉搜索的方式,效率不高,故一般需頻繁訪問的較大數據不適宜采用流式文件邏輯結構。但由于流式文件管理簡單,用戶可以較方便地對文件進行相關操作。

流的概念及分類

I/O 設備的多樣性及復雜性,給程序設計者訪問這些設備帶來了很大的難度和不便。為此,ANSIC 的 I/O 系統即標準 I/O 系統,把任意輸入的源端或任意輸出的終端,都抽象轉換成了概念上的“標準 I/O 設備”或稱“標準邏輯設備”。程序繞過具體設備,直接與該“標準邏輯設備”進行交互,這樣就為程序設計者提供了一個不依賴于任何具體 I/O 設備的統一操作接口,通常把抽象出來的“標準邏輯設備”或“標準文件”稱作“流”。

把任意 I/O 設備,轉換成邏輯意義上的“標準 I/O 設備”或“標準文件”的過程,并不需要程序設計者感知和處理,是由標準 I/O 系統自動轉換完成的。故從這個意義上,可以認為任意輸入的源端和任意輸出的終端均對應一個“流”。

流按方向分為:輸入流輸出流。從文件獲取數據的流稱為輸入流,向文件輸出數據稱為輸出流。

例如,從鍵盤輸入數據然后把該數據輸出到屏幕上的過程,相當于從一個文件輸入流(與鍵盤相關)中輸入(讀?。祿?,然后通過另外一個文件輸出流(與顯示器相關)把獲取的數據輸出(寫入)到文件(顯示器)上。

流按數據形式分為:文本流和二進制流。文本流是 ASCII 碼字符序列,而二進制流是字節序列。

文本文件與二進制文件

根據文件中數據的組織形式的不同,可以把文件分為:文本文件二進制文件。
  • 文本文件:把要存儲的數據當成一系列字符組成,把每個字符的 ASCII 碼值存入文件中。每個 ASCII 碼值占一個字節,每個字節表示一個字符。故文本文件也稱作字符文件或 ASCII 文件,是字符序列文件。
  • 二進制文件:把數據對應的二進制形式存儲到文件中,是字節序列文件。

例如數據 123,如果按文本文件形式存儲,把數據看成三個字符:'1'、'2'、'3' 的集合,文件中依次存儲各個字符的 ASCII 碼值,格式如表 1 所示。
表 1 數據 123 的文本存儲形式
字符 '1' '2' '3'
ASCII(十進制) 49 50 51
ASCII(二進制) 0011 0001 0011 0010 0011 0011

如果按照二進制文件形式存儲,則把數據 123 看成整型數,如果該系統中整型數占 4 個字節,則數據 123 二進制存儲形式的 4 個字節如下。

0000 0000 0000 0000 0000 0000 0111 1011

C語言與文件讀寫

C 程序與文件的訪問中,經常涉及換行操作。二進制文件與文本文件在換行規則上略有差別。

在 UNIX 和 Linux 系統中,無論是二進制文件還是文本文件,均是以單字節 LF(0x0A) 即作為文件中的換行符。

由于 C 語言是在 UNIX 系統上提出并發展起來的,故 C 語言中的換行規則與 UNIX 系統文件中的換行規則是一致的,使用 LF 即 '\n' 表示換行。因此 C 語言程序訪問 UNIX/Linux 系統中的文件時,可直接訪問,不需要轉換。

而在 DOS/Windows 系統中,文本文件使用 ASCII 值為 13(0x0D) 的回車符 CR(Carriage-Return) 以及 ASCII 值為 10(0x0A) 的換行符 LF(Line-Feed) 這兩個符號,即雙字節 CR-LF(0x0D 0x0A) 的 'r'、'\n' 作為文本文件的換行符。與 C 語言程序中的換行符不一致。

因此,若使用 C 語言程序訪問 DOS/Windows 系統中的文本文件,針對換行符的差異,就必須多一層轉換。如果把 C 程序中數據以文本的方式寫入文件時,需要把 C 程序中的 '\n' 轉換為 'r' 和 '\n' 這兩個字符后,再寫入文本文件;當 C 程序以文本方式讀取文本文件中的數據時,需要把文本文件中連續出現的兩個字符 'r'、'\n' 轉換為一個字符 '\n' 后,送給 C 程序。

說明:DOS/Windows 系統的文本文件中,回車 '\r' 和換行 '\n' 的含義如下:
  • 回車'\r':表示光標回到該行的行首處。
  • 換行'\n':表示光標從當前行該列位置移動到下一行對應的該列位置。

緩沖和非緩沖文件系統

C語言中文件系統可分為兩大類,一種是緩沖文件系統也稱為標準文件系統,另一種是非緩沖文件系統。ANSI C 標準中只采用緩沖文件系統。

緩沖文件系統:系統自動為每個打開的文件在內存開辟一塊緩沖區,緩沖區的大小一般由系統決定。當程序向文件中輸出(寫入)數據時,程序先把數據輸出到緩沖區,待緩沖區滿或數據輸出完成后,再把數據從緩沖區輸出到文件;當程序從文件輸入(讀取)數據時,先把數據輸入到緩沖區,待緩沖區滿或數據輸人完成后,再把數據從緩沖區逐個輸入到程序。

非緩沖文件系統:系統不自動為打開的文件開辟內存緩沖區,由程序設計者自行設置緩沖區及大小。

程序每一次訪問磁盤等外存文件都需要移動磁頭來定位磁頭扇區,如果程序頻繁地訪問磁盤文件,會縮短磁盤的壽命,況且速度較慢,與快速的計算機內存處理速度不匹配。

帶緩沖區文件系統的好處是減少對磁盤等外存文件的操作次數,先把數據讀?。▽懭耄┑骄彌_區中,相當于把緩沖區中的數據一次性與內存交互,提髙了訪問速度和設備利用率。

一般把帶緩沖文件系統的輸入輸出稱作標準輸入輸出(標準 I/O),而非緩沖文件系統的輸入輸出稱為系統輸入輸出(系統 I/O)。

ANSI C 為正在使用的每個文件分配一個文件信息區,該信息區中包含文件描述信息、 該文件所使用的緩沖區大小及緩沖區位置、該文件當前讀寫到的位置等基本信息。這些信息保存在一個結構體類型變量中,該結構體類型為 FILE 在 stdio.h 頭文件中定義,不允許用戶改變。

每個 C 編譯系統 stdio.h 文件中的 FILE 定義可能會稍有差別,但均包含文件讀寫的基本信息。

文件的打開與關閉

本節所涉及的文件如無特殊說明均指緩沖文件系統文件,即 ANSI C 標準文件。C 程序中對任何文件進行操作,都必須先“打開”文件,即打開流;操作完成后,需“關閉”文件,即關閉流。

這里的“打開”和“關閉”可調用標準庫 stdio.h 中的 fopen 和 fclose 函數實現。

打開函數 fopen 的原型如下。

FILE * fopen(char *filename, char *mode);

函數參數:
  1. filename:文件名,包括路徑,如果不顯式含有路徑,則表示當前路徑。例如,“D:\\f1.txt”表示 D 盤根目錄下的文件 f1.txt 文件。“f2.doc”表示當前目錄下的文件 f2.doc。
  2. mode:文件打開模式,指出對該文件可進行的操作。常見的打開模式如 “r” 表示只讀,“w” 表示只寫,“rw” 表示讀寫,“a” 表示追加寫入。更多的打開模式如表 2 所示。

表 2
模式 含 義 說 明
r 只讀 文件必須存在,否則打開失敗
w 只寫 若文件存在,則清除原文件內容后寫入;否則,新建文件后寫入
a 追加只寫 若文件存在,則位置指針移到文件末尾,在文件尾部追加寫人,故該方式不 刪除原文件數據;若文件不存在,則打開失敗
r+ 讀寫 文件必須存在。在只讀 r 的基礎上加 '+' 表示增加可寫的功能。下同
w+ 讀寫 新建一個文件,先向該文件中寫人數據,然后可從該文件中讀取數據
a+ 讀寫 在” a”模式的基礎上,增加可讀功能
rb 二進制讀 功能同模式”r”,區別:b表示以二進制模式打開。下同
wb 二進制寫 功能同模式w”。二進制模式
ab 二進制追加 功能同模式”a”。二進制模式
rb+ 二進制讀寫 功能同模式"r+”。二進制模式
wb+ 二進制讀寫 功能同模式”w+”。二進制模式
ab+ 二進制讀寫 功能同模式”a+”。二進制模式

返回值:打開成功,返回該文件對應的 FILE 類型的指針;打開失敗,返回 NULL。故需定義 FILE 類型的指針變量,保存該函數的返回值??筛鶕摵瘮档姆祷刂蹬袛辔募蜷_是否成功。

關閉函數 fclose 的原型如下。

int fclose(FILE *fp);

函數參數:
fp:已打開的文件指針。

返回值:正常關閉,返回否則返回 EOF(-1)。

例如:
FILE *fpl,*fp2; //定義兩個文件指針變量fpl和fp2
fpl=fopen(”D:\\fl.txt”,”r”); //以只讀模式打開文件 fl.txt
if (NULL==fpl) //以返回值fpl判斷是否打開成功,如果為NULL表示失敗
{
    printf ("Failed to open the file !\n");
    exit (0) ; //終止程序,stdlib .h頭文件中
}
fp2=fopen ("f2.txt","a") ; //以追加寫入的模式打開文件f2 .txt
if(NULL==fp2)
{
    printf ("Failed to open the file !\n");
    exit (0);
}
fclose (fpl); //關閉fpl指針對應文件(fl.txt)的流
fclose (fp2); //關閉fp2指針對應文件(f2.txt)的流

文件的順序讀寫

對文件讀取操作完成后,如果從文件中讀取到的每個數據的順序與文件中該數據的物理存放順序保持一致,則稱該讀取過程為順序讀??;同理,對文件寫入操作完成后,如果文件中所有數據的存放順序與各個數據被寫入的先后順序保持一致,則稱該寫入過程為順序寫入。

換字符輸入輸出

c 語言中提供了從文件中逐個輸入字符及向文件中逐個輸出字符的順序讀寫函數 fgetc 和 fputc 及調整文件讀寫位置到文件開始處的函數 rewind。這些函數均在標準輸入輸出頭文件 stdio.h 中。

字符輸入函數 fgetc 的函數原型為:
int fgetc (FILE *fp);
所在頭文件:<stdio.h>。

函數功能:從文件指針 fp 所指向的文件中輸入一個字符。輸入成功,返回該字符;已讀取到文件末尾,或遇到其他錯誤,即輸入失敗,則返回文本文件結束標志 EOF(EOF 在 stdio.h 中已定義,一般為 -1)。

注意:由于 fgetc 是以 unsigned char 的形式從文件中輸入(讀?。┮粋€字節,并在該字節前面補充若干 0 字節,使之擴展為該系統中的一個 int 型數并返回,而非直接返回 char 型。當輸入失敗時返回文本文件結束標志 EOF 即 -1,也是整數。故返回類型應為 int 型,而非 char 型。

如果誤將返回類型定義為 char 型,文件中特殊字符的讀取可能會出現意想不到的邏輯錯誤。

由于在 C 語言中把除磁盤文件外的輸入輸出設備也當成文件處理,故從鍵盤輸入字符不僅可以使用宏 getchar() 實現,也可以使用 fgetc (stdin) 實現。其中,stdin 指向標準輸入設備—鍵盤所對應的文件。stdin 不需要人工調用函數 fopen 打開和 fclose 關閉。

字符輸出函數 fputc 的函數原型為:
int fputc (int c, FILE *fp);
所在頭文件:<stdio.h>

函數功能:向 fp 指針所指向的文件中輸出字符 c,輸出成功,返回該字符;輸出失敗,則返回 EOF(-1)。

向標準輸出設備屏幕輸出字符變量 ch 中保存的字符,不僅可以使用宏 putchar(ch) 實現,也可以使用 fputc (ch,stdout); 實現。其中,stdout 指向標準輸出設備—顯示器所對應的文件。stdout 也不需要人工調用函數 fopen 打開和 fclose 關閉。

對一個文件進行讀寫操作時,經常會把一個文件中讀寫位置重新調整到文件的開始處,可以使用函數 rewind 實現。

文件讀寫位置復位函數 rewind 的函數原型為:

void rewind (FILE *fp);

所在頭文件:<stdio.h>

函數功能:把 fp 所指向文件中的讀寫位置重新調整到文件開始處。

【例 1】從鍵盤輸入若干個字符,同時把這些字符輸出到 D 盤根目錄下的文件 data_file.txt 中及屏幕上。各個字符連續輸入,最后按下回車鍵結束輸入過程。

實現代碼為:
#include<stdio.h>
#include<stdlib.h>
int main (void)
{
    char file_name[20]="D:/data—file.txt";
    FILE * fp=fopen (file_name, "w") ; //打開文件
    int c; //c:接收fgetc的返回值,定義為int,而非char M
    if(NULL==fp)
    {
        printf ("Failed tO open the file !\n");
        exit(0);
    }
    printf ("請輸入字符,按回車鍵結束:");
    while ((c=fgetc (stdin)) != '\n') //stdin:指向標準輸人設備鍵盤文件
    {
        fputc (c, stdout); //stdout:指向標準輸出設備顯示器文件
        fputc(c,fp);
    }
    fputc ('\n', stdout);
    fclose (fp); //關閉文件
    return 0;
}
輸出結果為:
請輸入字符,按回車鍵結束:I love C
I love C

此時,查看 D 盤根目錄下生成的 data_file.txt 文件,并且其內容為 I love C。

接字符串輸入輸出

下面主要介紹文件中常見的字符串輸入、輸出函數 fgets 和 fputs。

字符串輸入函數 fgets 的函數原型為:

char * fgets (char *s, int size, FILE * fp);

所在頭文件:<stdio.h>

函數功能:從 fp 所指向的文件內,讀取若干字符(一行字符串),并在其后自動添加字符串結束標志 '\0' 后,存入 s 所指的緩沖內存空間中(s 可為字符數組名),直到遇到回車換行符或已讀取 size-1 個字符或已讀到文件結尾為止。該函數讀取的字符串最大長度為 size-1。

參數 fp:可以指向磁盤文件或標準輸入設備 stdin。

返回值:讀取成功,返回緩沖區地址 s;讀取失敗,返回 NULL。

說明:fgets 較之 gets 字符串輸入函數是比較安全規范的。因為 fgets 函數可由程序設計者自行指定輸入緩沖區 s 及緩沖區大小 size。即使輸入的字符串長度超過了預定的緩沖區大小,也不會因溢出而使程序崩潰,而是自動截取長度為 size-1 的串存入 s 指向的緩沖區中。
 
字符串輸出函數 fputs 的函數原型為:

int fputs (const char *str, FILE *fp);

所在頭文件:<stdio.h>

函數功能:把 str(str 可為字符數組名)所指向的字符串,輸出到 fp 所指的文件中。

返回值:輸出成功,返回非負數;輸出失敗,返回EOF(-1)。

【例 2】從鍵盤輸入若干字符串存入 D 盤根目錄下文件 file.txt 中,然后從該文件中讀取所有字符串并輸出到屏幕上。

實現代碼為:
#include<stdio.h>
#include<stdlib.h>
#define N 3 //字符串個數
#define MAX_SIZE 30 //字符數組大小,要求每個字符串長度不超過29
int main (void)
{
    char file_name[30]="D:\\file.txt";
    char str[MAX_SIZE];
    FILE *fp;
    int i;
    fp=fopen (file_name, "w+") ; //"w+"模式:先寫入后讀出
    if(NULL==fp)
    {
        printf ("Failed to open the file !\n");
        exit (0);
    }
    printf ("請輸入%d個字符串:\n",N);
    for(i=0;i<N;i++)
    {
        printf ("字符串%d:",i+1);
        fgets (str,MAX_SIZE, stdin) ;//從鍵盤輸入字符串,存入str數組中
        fputs (str, fp) ;//把str中字符串輸出到fp所指文件中
    }
    rewind (fp); //把fp所指文件的讀寫位置調整為文件開始處
    while (fgets(str,MAX_SIZE,fp) !=NULL)
    {
        fputs (str, stdout) ; //把字符串輸出到屏幕
    }
    fclose(fp);
    return 0;
}
運行結果為:
請輸入3個字符串:
字符串1:How are you going today?
字符串2:Never speak die.
字符串3:Good job!
How are you going today?
Never speak die.
Good job!

此時,D 盤目錄下已生成 file.txt 文件,其內容同輸出結果完全相同。

按格式化輸入輸出

文件操作中的格式化輸入輸出函數 fscanf 和 fprintf 一定意義上就是 scanf 和 printf 的文本版本。程序設計者可根據需要采用多種格式靈活處理各種類型的數據,如整型、字符型、浮點型、字符串、自定義類型等。

文件格式化輸入函數 fscanf 的函數原型為:

int fscanf (文件指針,格式控制串,輸入地址表列);

所在頭文件:<stdio.h>

函數功能:從一個文件流中執行格式化輸入,當遇到空格或者換行時結束。注意該函數遇到空格時也結束,這是其與 fgets 的區別,fgets 遇到空格不結束。

返回值:返回整型,輸入成功時,返回輸入的數據個數;輸入失敗,或已讀取到文件結尾處,返回 EOF(-1)。

故一般可根據該函數的返回值是否為 EOF 來判斷是否已讀到文件結尾處。

例如,若文件 f1.dat 中保存了若干整數,各整數之間用空格間隔,從文件中讀取兩個整數,依次保存到兩個整型變量中。程序代碼段如下。
int a,b;
FILE *fp=fopen("f1.dat","r");
if(NULL==fp)
{
    printf ("Failed to open the file!\n");
    exit (0);
}
fscanf (fp,"%d%d",&a,&b) ; //從fp所指文件中讀取一個整數保存到變量a中
fclose(fp);
如果 f1.dat 中的整數用逗號間隔,則讀取兩個整數時,函數 fscanf 的調用格式如下所示。
fscanf (fp,"%d,%d", &a, &b); //兩個%d之間也必須用逗號隔開
文件格式化輸出函數 fprintf 的函數原型為:

int fprintf (文件指針,格式控制串,輸出表列);

所在頭文件:<stdio.h>

函數功能:把輸出表列中的數據按照指定的格式輸出到文件中。

返回值:輸出成功,返回輸出的字符數;輸出失敗,返回一負數。

例如,向當前目錄文件file.txt中輸入一個學生的姓名、學號和年齡,采用文本方式,參考代碼如下。
#include<stdio.h>
#include<stdlib.h>
int main (void)
{
    FILE *fp=fopen("file.txt","w");
    char name[ 10] ="張三";
    char no[15]="20170304007";
    int age=17;
    if(NULL==fp)
    {
        printf ("Failed to open the file !\n");
        exit (0);
    }
    fprintf(fp,"%s\t%s\t%d\n",name,no,age);
    fclose(fp);
    return 0;
}
運行程序后,當前目錄下生成了 file.txt 文件,并且其內容為:
張三  20170304007  17

按二進制方式讀寫數據塊

接下來介紹按塊讀寫數據的函數 fread 和 fwrite,這兩個函數主要應用于對二進制文件的讀寫操作,不建議在文本文件中使用。接著介紹了 fread 讀取二進制文件時,判斷是否已經到達文件結尾的函數 feof。

數據塊讀?。ㄝ斎耄┖瘮?fread 的函數原型為:

unsigned fread (void *buf, unsigned size, unsigned count, FILE* fp);

所在頭文件:<stdio.h>

函數功能:從 fp 指向的文件中讀取 count 個數據塊,每個數據塊的大小為 size。把讀取到的數據塊存放到 buf 指針指向的內存空間中。

返回值:返回實際讀取的數據塊(非字節)個數,如果該值比 count 小,則說明已讀到文件尾或有錯誤產生。這時一般采用函數 feof 及 ferror 來輔助判斷。

函數參數:
  • buf:指向存放數據塊的內存空間,該內存可以是數組空間,也可以是動態分配的內存。void類型指針,故可存放各種類型的數據,包括基本類型及自定義類型等。
  • size:每個數據塊所占的字節數。
  • count:預讀取的數據塊最大個數。
  • fp:文件指針,指向所讀取的文件。

數據塊寫入(輸出)函數 fwrite 的函數原型為:

unsigned fwrite (const void *bufAunsigned size,unsigned count,FILE* fp);

所在頭文件:<stdio.h>

函數功能:將 buf 所指向內存中的 count 個數據塊寫入 fp 指向的文件中。每個數據塊的大小為 size。

返回值:返回實際寫入的數據塊(非字節)個數,如果該值比 count 小,則說明 buf 所指空間中的所有數據塊已寫完或有錯誤產生。這時一般采用 feof 及 ferror 來輔助判斷。

函數參數:
  • buf:前加const的含義是buf所指的內存空間的數據塊只讀屬性,避免程序中有意或無意的修改。
  • size:每個數據塊所占的字節數。
  • count:預寫入的數據塊最大個數。
  • fp:文件指針,指向所讀取的文件。
注意:使用 fread 和 fwrite 對文件讀寫操作時,一定要記住使用“二進制模式”打開文件,否則,可能會出現意想不到的錯誤。

在操作文件時,經常使用 feof 函數來判斷是否到達文件結尾。

feof 函數的函數原型為:

int feof (FILE * fp);

所在頭文件:<stdio.h>

函數功能:檢查 fp 所關聯文件流中的結束標志是否被置位,如果該文件的結束標志已被置位,返回非 0 值;否則,返回 0。

需要注意的是:
1) 在文本文件和二進制文件中,均可使用該函數判斷是否到達文件結尾。

2) 文件流中的結束標志,是最近一次調用輸入等相關函數(如 fgetc、fgets、fread 及 fseek 等)時設置的。只有最近一次操作輸入的是非有效數據時,文件結束標志才被置位;否則,均不置位。

【例 3】從鍵盤輸入若干名學生的姓名、學號、語數外三門課成績并計算平均成績,將這些學生信息以二進制方式保存到當前目錄文件 Stia_Info.dat 中。采用 fwrite 函數寫入數據。存儲空間要求采用數組形式。采用靜態數組形式,僅為了復習數組作為函數參數的情況,且便于理解,實際編程中不建議采用這種方案。

實現代碼為:
#include<stdio.h>
#include<stdlib.h>
typedef struct {
    char name[10];
    char no[15];
    int sc[3];
    float aver;
}STU;
void Input_Info(STU a[],int n) ; //輸入函數原型聲明
void Write_Info (STU a[],int n) ; //文件寫入函數原型聲明
#define N 10 //最多可存儲的學生數,可調整
int main (void)
{
    int n;
    STU a[N]; //學生數組,最多容納N人
    printf ("輸入學生人數:");
    scanf("%d",&n);
    Input_Info (a,n) ; //輸入學生信息
    Write_Info (a,n); //寫人文件
    return 0;
}

void Input_Info (STU a[], int n)
{
    int i;
    for(i=0;i<n;i++)
    {
        printf ("%dth stu (姓名、學號、語數外):",i+1);
        scanf("%s%s%d%d%d",a[i].name,a[i].no,&a[i].sc[0],&a[i].sc[1],&a[i].sc[2]);
        a[i].aver= (a[i].sc[0]+a[i].sc[1]+a[i].sc[2])/3.0;
    }
}
void Write_Info (STU a[], int n)
{
    FILE *fp=fopen ("Stu_Info.dat","wb") ; //"wb":二進制文件寫操作
    if(NULL==fp)
    {
        printf ("Failed to open the file !\n");
        exit (0);
    }
    fwrite (a, sizeof (STU) ,n, fp) ; //把a數組中n個學生信息寫入文件
    fclose (fp);
}
由于采用二進制形式存儲,故打開生成的二進制文件 Stu_Info.dat 可能是“亂碼”。通過判斷文件的生成以及文件中部分顯示正常的數據,可判斷代碼是否運行正確。

文件的隨機讀寫

以上介紹的都是文件的順序讀寫操作,即每次只能從文件頭開始,從前往后依次讀寫文件中的數據。在實際的程序設計中,經常需要從文件的某個指定位置處開始對文件進行選擇性的讀寫操作,這時,首先要把文件的讀寫位置指針移動到指定處,然后再進行讀寫,這種讀寫方式稱為對文件的隨機讀寫操作。

C 語言程序中常使用 rewind、fseek 函數移動文件讀寫位置指針。使用 ftell 獲取當前文件讀寫位置指針。

函數 fseek 的函數原型為:

int fseek(FI:LE *fp, long offset, int origin);

所在頭文件:<stdio.h>

函數功能:把文件讀寫指針調整到從 origin 基點開始偏移 offset 處,即把文件讀寫指針移動到 origin+offset 處。

函數參數:

1) origin:文件讀寫指針移動的基準點(參考點)?;鶞饰恢?origin 有三種常量取值:SEEK_SET、SEEK_CUR 和 SEEK_END,取值依次為 0,1,2。

SEEK_SET:文件開頭,即第一個有效數據的起始位置。
SEEK_CUR:當前位置。
SEEK_END:文件結尾,即最后一個有效數據之后的位置。注意:此處并不能讀取到最后一個有效數據,必須前移一個數據塊所占的字節數,使該文件流的讀寫指針到達最后一個有效數據塊的起始位置處。

2) offset:位置偏移量,為 long 型,當 offset 為正整數時,表示從基準 origin 向后移動 offset 個字節的偏移;若 offset 為負數,表示從基準 origin 向前移動 |offset| 個字節的偏移。

返回值:成功,返回 0;失敗,返回 -1。

例如,若 fp 為文件指針,則 seek (fp,10L,0); 把讀寫指針移動到從文件開頭向后 10 個字節處。 fSeek(fp,10L,1); 把讀寫指針移動到從當前位置向后 10 個字節處。 fseek(fp,-20L,2); 把讀寫指針移動到從文件結尾處向前 20 個字節處。

調用 fseek 函數時,第三個實參建議不要使用 0、1、2 等數字,最好使用可讀性較強的常量符號形式,使用如下格式取代上面三條語句。

fseek(fp,10L,SEEK_SET);
fseek(fp,10L,SEEK_CUR);
fseek(fp,-20L,SEEK_END);

函數 ftell 的函數原型:

long ftell (FILE *fp);

所在頭文件:<stdio.h>

函數功能:用于獲取當前文件讀寫指針相對于文件頭的偏移字節數。

例如,分析以下程序,輸出其運行結果。
#include<stdio.h>
#include<stdlib.h>
#define N 3 //動物數
typedef struct {
    char name[10];
    int age;
    char duty[20];
}Animal;
int main (void)
{
    Animal a[N] = {{"兔朱迪",5, "交通警察"}, {"尼克", 8, "協警"},{"閃電",10, "車管所職工"}},t;
    int i;
    FILE *fp=fopen ("Animal_Info.bat", "wb+");
    if(NULL==fp)
    {
        printf("Failed to open the file!\n");
        exit (0);
    }
    fwrite(a,sizeof(Animal),N,fp);
    fprintf (stdout, "%s\t%s\t%s\n", "名字","年齡","職務");
    for(i=1;i<=N;i++)
    {
        fseek(fp,0-i*sizeof(Animal),SEEK_END);
        fread(&t,sizeof(Animal),1,fp);
        fprintf (stdout, "%s\t%d\t%-s\n", t.name,t.age,t.duty);
    }
    fclose(fp);
    return 0;

}
運行結果為:
名字    年齡    職務
閃電    10      車管所職工
尼克    8       協警
兔朱迪  5       交通警察

精美而實用的網站,提供C語言、C++、STL、Linux、Shell、Java、Go語言等教程,以及socket、GCC、vi、Swing、設計模式、JSP等專題。

Copyright ?2011-2018 biancheng.net, 陜ICP備15000209號

底部Logo
极速pk10开户