1. 程式人生 > >C語言復習:文件操作

C語言復習:文件操作

end 文件 don 表示 creat 比較 部分 name conf

文件操作專題

C語言文件讀寫概念

文件分類

  • 按文件的邏輯結構:
    • 記錄文件:由具有一定結構的記錄組成(定長和不定長)
    • 流式文件:由一個個字符(字節)數據順序組成
  • 按存儲介質:
    • 普通文件:存儲介質文件(磁盤、磁帶等)
    • 設備文件:非存儲介質(鍵盤、顯示器、打印機等)
  • 按數據的組織形式:
    • 文本文件: ASCII文件,每個字節存放一個字符的ASCII碼
    • 二進制文件:數據按其在內存中的存儲形式原樣存放

技術分享圖片

  • 每個文件都以文件名為標識,I/O設備的文件名是系統定義的,如:
  • COM1或AUX——第一串行口,附加設備
  • COM2——第二串行口,此外,還可能有COM3、COM4等
  • CON——控制臺(console),鍵盤(輸入用)或顯示器(輸出用)
  • LPT1或PRN——第一並行口或打印機
  • LPT2——第二並行口,還可能有LPT3等
  • NUL——空設備 (不是NULL)
  • 磁盤文件可以由用戶自己命名,但上述被系統(windows和dos下均是如此)保留的設備名字不能用作文件名,如不能把一個文件命名為CON(不帶擴展名)或CON.TXT(不帶擴展名)。
  • 流概念:
  • 流是一個動態的概念,可以將一個字節形象地比喻成一滴水,字節在設備、文件和程序之間的傳輸就是流,類似於水在管道中的傳輸,可以看出,流是對輸入輸出源的一種抽象,也是對傳輸信息的一種抽象。通過對輸入輸出源的抽象,屏蔽了設備之間的差異,使程序員能以一種通用的方式進行存儲操作,通過對傳輸信息的抽象,使得所有信息都轉化為字節流的形式傳輸,信息解讀的過程與傳輸過程分離。
  • C語言中,I/O操作可以簡單地看作是從程序移進或移出字節,這種搬運的過程便稱為流(stream)。程序只需要關心是否正確地輸出了字節數據,以及是否正確地輸入了要讀取字節數據,特定I/O設備的細節對程序員是隱藏的。
  • 文件處理方法
  • 緩沖文件系統:高級文件系統,系統自動為正在使用的文件開辟內存緩沖區
  • 非緩沖文件系統:低級文件系統,由用戶在程序中為每個文件設定緩沖區
  • 技術分享圖片
  • 緩沖文件系統理解:文件句柄
  • 技術分享圖片
  • 補充一個:查看當前系統的IO緩沖區大小是通過宏BUFSIZ,stdio.h中找,可以看到大小。

文件操作API

文件api的分類

  1. 文件讀寫api

fgetc int fgetc( FILE *stream );

fputc int fputc( int ch, FILE *stream );

按照字符讀寫文件

char c;

FILE fd;

ch=fgetc(fd);

fd=fputc(c,fd); //假設fd已經被讀寫方式打開

fputc()會返回寫入成功的字符, 若返回EOF(-1) 則代表寫入失敗

getc()會返回讀取到的字符, 若返回EOF 則表示到了文件尾.

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

fgets char *fgets( char *str, int num, FILE *stream );

按照行讀寫文件 (讀寫配置文件)

char ch[1024];

fgets(ch,sizeof(ch),fd)

fputs(ch,fd);

fgets()成功,則返回第一個參數str,失敗返回NULL;

fputs()成功返回寫入的字符數;失敗返回EOF

fread int fread( void *buffer, size_t size, size_t num, FILE *stream );

fwirte int fwrite( const void *buffer, size_t size, size_t count, FILE *stream );

按照塊讀寫文件 (大數據塊遷移)

fread(ch,sizeof(char),sizeof(ch)-1,fd);

fwrite(ch,sizeof(char),sizeof(ch)-1,fd);

返回值:返回實際寫入的數據塊數目(參數3);若是返回值大於參數3,則發生錯誤。

我嘗試過利用fread函數賦值二維數組,但是失敗了.

fprintf int fprintf( FILE *stream, const char *format, ... );

fscanf  int fscanf( FILE *stream, const char *format, ... );

按照格式化進行讀寫文件

fprintf(fd,"%s",ch);

fscanf(fd,"%c",c)

成功返回工作的數目;失敗返回負數

02)文件控制api

文件是否結束

文件指針的定位、跳轉

fseek(fp, 0L, SEEK_END); //把文件指針從0位置開始,移動到文件末尾(宏的作用),那個0L是將0轉化為long型

宏: 偏移起始位置:文件頭0(SEEK_SET),當前位置1(SEEK_CUR),文件尾2(SEEK_END)

//獲取文件長度;

得到文件位置指針當前位置相對於文件首的偏移字節數。

length = ftell(fp);

fseek(fp, 0L, SEEK_SET);

標準文件的讀寫

1.文件的打開fopen()

文件的打開操作表示將給用戶指定的文件在內存分配一個FILE結構區,並將該結構的指針返回給用戶程序,以後用戶程序就可用此FILE指針來實現對指定文件的存取操作了。當使用打開函數時,必須給出文件名、文件操作方式(讀、寫或讀寫),如果該文件名不存在,就意味著建立(只對寫文件而言,對讀文件則出錯),並將文件指針指向文件開頭。若已有一個同名文件存在,則刪除該文件,若無同名文件,則建立該文件,並將文件指針指向文件開頭。

fopen(char *filename,char *type);

其中*filename是要打開文件的文件名指針,一般用雙引號括起來的文件名表示,也可使用雙反斜杠隔開的路徑名。而*type參數表示了對打開文件的操作方式。其可采用的操作方式如下:

方式 含義

"r" 打開,只讀

"w" 打開,文件指針指到頭,只寫

"a" 打開,指向文件尾,在已存在文件中追加

"rb" 打開一個二進制文件,只讀

"wb" 打開一個二進制文件,只寫

"ab" 打開一個二進制文件,進行追加

"r+" 以讀/寫方式打開一個已存在的文件

"w+" 以讀/寫方式建立一個新的文本文件

"a+" 以讀/寫方式打開一個文件文件進行追加

"rb+" 以讀/寫方式打開一個二進制文件

"wb+" 以讀/寫方式建立一個新的二進制文件

"ab+" 以讀/寫方式打開一個二進制文件進行追加

當用fopen()成功的打開一個文件時,該函數將返回一個FILE指針,如果文件打開失敗,將返回一個NULL指針。如想打開test文件,進行寫:

FILE *fp;//聲明指針,而不是實例.

if((fp=fopen("test","w"))==NULL)

{

printf("File cannot be opened\n");

exit();

}

else

printf("File opened for writing\n");

……

fclose(fp);

DOS操作系統對同時打開的文件數目是有限制的,缺省值為5,可以通過修改CONFIG.SYS文件改變這個設置。

2.關閉文件函數fclose()

文件操作完成後,必須要用fclose()函數進行關閉,這是因為對打開的文件進行寫入時,若文件緩沖區的空間未被寫入的內容填滿,這些內容不會寫到打開的文件中去而丟失。只有對打開的文件進行關閉操作時,停留在文件緩沖區的內容才能寫到該文件中去,從而使文件完整。再者一旦關閉了文件,該文件對應的FILE結構將被釋放,從而使關閉的文件得到保護,因為這時對該文件的存取操作將不會進行。文件的關閉也意味著釋放了該文件的緩沖區。

int fclose(FILE *stream);

它表示該函數將關閉FILE指針對應的文件,並返回一個整數值。若成功地關閉了文件,則返回一個0值,否則返回一個非0值。常用以下方法進行測試:

if(fclose(fp)!=0)

{

printf("File cannot be closed\n");

exit(1);

}

else

printf("File is now closed\n");

當打開多個文件進行操作,而又要同時關閉時,可采用fcloseall()函數,它將關閉所有在程序中打開的文件。

int fcloseall();//關閉除標準流(stdinstdoutstderrstdprnstdaux)之外的所有打開的流,刷新所有的流緩沖區,並返回關閉的流數。

該函數將關閉所有已打開的文件,將各文件緩沖區未裝滿的內容寫到相應的文件中去,接著釋放這些緩沖區,並返回關閉文件的數目。如關閉了4個文件,則當執行:

n=fcloseall(); 時,n應為4。

3.文件的讀寫

(1).讀寫文件中字符的函數(一次只讀寫文件中的一個字符)

int fgetc(FILE *stream);

int fgetchar(void);

int fputc(int ch,FILE *stream);

int fputchar(int ch);

int getc(FILE *stream);

int putc(int ch,FILE *stream);

其中fgetc()函數將把由流指針指向的文件中的一個字符讀出,例如:

ch=fgetc(fp);

將把流指針fp指向的文件中的一個字符讀出,並賦給ch,當執行fgetc()函數時,若當時文件指針指到文件尾,即遇到文件結束標誌EOF(其對應值為-1),該函數返回一個-1給ch,在程序中常用檢查該函數返回值是否為-1來判斷是否已讀到文件尾,從而決定是否繼續。

#include "stdio.h"

main()

{

FILE *fp;

char ch;

if((fp=fopen("myfile.tex","r"))==NULL)

{

printf("file cannot be opened\n");

exit(1);

while((ch=fgetc(fp))!=EOF) fputc(ch,stdout);

close(fp);

}

該程序以只讀方式打開myfile.txt文件,在執行while循環時,文件指針每循環一次後移一個字符位置。用fgetc()函數將文件指針指定的字符讀到ch變量中,然後用fputc()函數在屏幕上顯示,當讀到文件結束標誌EOF時,變關閉該文件。

上面的程序用到了fputc()函數,該函數將字符變量ch的值寫到流指針指定的文件中去,由於流指針用的是標準輸出(顯示器)的FILE指針stdout,故讀出的字符將在顯示器上顯示。又比如:

putc(ch,fp);

該函數執行結構,將把ch表示的字符送到流指針fp指向的文件中去。

在Turbo C編譯器中,putc()等價於fput(),getc()等價於fgetc()。putchar(c)相當於fputc(c,stdout);getchar()相當於fgetc(stdin)。

註意,這裏使用char ch,其實是不科學的,因為最後判斷結束標誌時,是看ch!=EOF,而EOF的值為-1,這顯然和char是不能比較的。所以,某些使用,我們都定義成int ch。

(2).讀寫文件中字符串的函數

char *fgets(char *string,int n,FILE *stream);

char *gets(char *s);

int fprintf(FILE *stream,char *format,variable-list);

int fputs(char *string,FILE *stream);

int fscanf(FILE *stream,char *format,variable-list);

其中fgets()函數將把由流指針指定的文件中n-1個字符,讀到由指針stream指向的字符數組中去,例如:

fgets(buffer,9,fp);

將把fp指向的文件中的8個字符讀到buffer內存區,buffer可以是定義的字符數組,也可以是動態分配的內存區。

註意,fgets()函數讀到‘\n‘就停止,而不管是否達到數目要求。同時在讀取字符串的最後加上‘\0‘。

fgets()函數執行完以後,返回一個指向該串的指針。如果讀到文件尾或出錯,則均返回一個空指針NULL,所以長用feof()函數來測定是否到了文件尾或者是ferror()函數來測試是否出錯,例如下面的程序用fgets()函數讀test.txt文件中的第一行並顯示出來:

#include "stdio.h"

main()

{

FILE *fp;

char str[128];

if((fp=fopen("test.txt","r"))==NULL)

{

printf("cannot open file\n");

exit(1);

}

while(!feof(fp))

{

if(fgets(str,128,fp)!=NULL) printf("%s",str);

}

fclose(fp);

}

gets()函數執行時,只要未遇到換行符或文件結束標誌,將一直讀下去。因此讀到什麽時候為止,需要用戶進行控制,否則可能造成存儲區的溢出。因此被認為不安全,逐漸不使用它。

fputs()函數想指定文件寫入一個由string指向的字符串,‘\0‘不寫入文件。

fprintf()和fscanf()同printf()和scanf()函數類似,不同之處就是printf()函數是想顯示器輸出,fprintf()則是向流指針指向的文件輸出;fscanf()是從文件輸入。

下面程序是向文件test.dat裏輸入一些字符:

#include<stdio.h>

main()

{

char *s="That‘s good news";

int i=617;

FILE *fp;

fp=fopne("test.dat", "w");

fputs("Your score of TOEFLis",fp);

fputc(‘:‘, fp);

fprintf(fp, "%d\n", i);

fprintf(fp, "%s", s);

fclose(fp);

}

用DOS的TYPE命令顯示TEST.DAT的內容如下所示:

屏幕顯示

Your score of TOEFL is: 617

That‘s good news

下面的程序是把上面的文件test.dat裏的內容在屏幕上顯示出來:

#include<stdio.h>

main()

{

char *s, m[20];

int i;

FILE *fp;

fp=fopen("test.dat", "r");

fgets(s, 24, fp);

printf("%s", s);

fscanf(fp, "%d", &i);

printf("%d", i);

putchar(fgetc(fp));

fgets(m, 17, fp);

puts(m);

fclose(fp);

getch();

}

運行後屏幕顯示:

Your score of TOEFL is: 617

That‘s good news

4.清除和設置文件緩沖區

(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()函數打開文件設定的默認緩沖區。

對於setbuf()函數,buf指出的緩沖區長度由頭文件stdio.h中定義的宏BUFSIZ的值決定,缺省值為512字節(不同的系統不一樣,我的Linux測試是8192字節,還有,他就是BUFSIZ,沒有E字母)。當選定buf為空時,setbuf函數將使的文件I/O不帶緩沖(直接讀寫文件)。而對setvbuf函數,則由malloc函數來分配緩沖區。參數size指明了緩沖區的長度(必須大於0),而參數type則表示了緩沖的類型,其值可以取如下值:

type 值 含義

_IOFBF 文件全部緩沖,即緩沖區裝滿後,才能對文件讀寫

_IOLBF 文件行緩沖,即緩沖區接收到一個換行符時,才能對文件讀寫

_IONBF 文件不緩沖,此時忽略buf,size的值,直接讀寫文件,不再經過文件緩沖區緩沖

5.文件的隨機讀寫函數

前面介紹的文件的字符/字符串讀寫,均是進行文件的順序讀寫,即總是從文件的開頭開始進行讀寫。這顯然不能滿足我們的要求,C語言提供了移動文件指針和隨機讀寫的函數,它們是:

(1).移動文件指針函數

long ftell(FILE *stream); 函數ftell()用來得到文件指針離文件開頭的偏移量。當返回值是-1時表示出錯。

int rewind(FILE *stream); rewind()函數用於文件指針移到文件的開頭,當移動成功時,返回0,否則返回一個非0值。

fseek(FILE *stream,long offset,int origin); fseek()函數用於把文件指針以origin為起點移動offset個字節,其中origin指出的位置可有以下幾種:

origin 數值 代表的具體位置

SEEK_SET 0 文件開頭

SEEK_CUR 1 文件指針當前位置

SEEK_END 2 文件尾

例如:

fseek(fp,10L,0);//0代表文件頭

把文件指針從文件開頭移到第10字節處,由於offset參數要求是長整型數,故其數後帶L。

fseek(fp,-15L,2);//2代表文件尾

把文件指針從文件尾向前移動15字節。

(2).文件隨機讀寫函數

int fread(void *ptr,int size,int nitems,FILE *stream);

int fwrite(void *ptr,int size,int nitems,FILE *stream);

fread()函數從流指針指定的文件中讀取nitems個數據項,每個數據項的長度為size個字節,讀取的nitems數據項存入由ptr指針指向的內存緩沖區中,在執行fread()函數時,文件指針隨著讀取的字節數而向後移動,最後移動結束的位置等於實際讀出的字節數。該函數執行結束後,將返回實際讀出的數據項數,這個數據項數不一定等於設置的nitems,因為若文件中沒有足夠的數據項,或讀中間出錯,都會導致返回的數據項數少於設置的nitems。當返回數不等於nitems時,可以用feof()或ferror()函數進行檢查。

fwrite()函數從ptr指向的緩沖區中取出長度為size字節的nitems個數據項,寫入到流指針stream指向的文件中,執行該操作後,文件指針將向後移動,移動的字節數等於寫入文件的字節數目。該函數操作完成後,也將返回寫入的數據項數。

非標準文件的讀寫

這類函數最早用於UNIX操作系統,ANSI標準未定義,但有時也經常用到,DOS 3.0以上版本支持這些函數。它們的頭文件為io.h。

由於我們不常用這些函數,所以在這裏就簡單說一下。

1.文件的打開和關閉

open()函數的作用是打開文件,其調用格式為:

int open(char *filename, int access);

該函數表示按access的要求打開名為filename的文件,返回值為文件描述字,其中access有兩部分內容:

基本模式和修飾符, 兩者用" "("或")方式連接。修飾符可以有多個, 但基本模式只能有一個。

access的規定

--------------------------------------------------------

基本模式 含義 修飾符 含 義

--------------------------------------------------------

O_RDONLY 只讀 O_APPEND 文件指針指向末尾

O_WRONLY 只寫 O_CREAT 文件不存在時創建文件, 屬性按基本模式屬性

O_RDWR 讀寫 O_TRUNC 若文件存在, 將其長度縮為0, 屬性不變

O_BINARY 打開一個二進制文件

O_TEXT 打開一個文字文件

---------------------------------------------------------

open()函數打開成功, 返回值就是文件描述字的值(非負值), 否則返回-1。

close()函數的作用是關閉由open()函數打開的文件, 其調用格式為:

int close(int handle);

該函數關閉文件描述字handle相連的文件。

2.讀寫函數

int read(int handle, void *buf, int count);

read()函數從handle(文件描述字)相連的文件中, 讀取count個字節放到buf所指的緩沖區中,

返回值為實際所讀字節數, 返回-1表示出錯。返回0 表示文件結束。

write()函數的調用格式為:

int write(int handle, void *buf, int count);

write()函數把count個字節從buf指向的緩沖區寫入與handle相連的文件中, 返回值為實際寫入的字節數。

3.隨機定位函數

lseek()函數的調用格式為:

int lseek(int handle, long offset, int fromwhere);

該函數對與handle相連的文件位置指針進行定位,功能和用法與fseek()函數相同。

tell()函數的調用格式為:

long tell(int handle);

該函數返回與handle相連的文件現生位置指針, 功能和用法與ftell()相同

註意點

文本文件: ASCII文件,每個字節存放一個字符的ASCII碼

二進制文件:數據按其在內存中的存儲形式原樣存放

技術分享圖片

項目開發中參考fgets函數的實現方法

fgets(buf, bufMaxLen, fp);

對fgets函數來說,n必須是個正整數,表示從文件按中讀出的字符數不超過n-1,存儲到字符數組str中,並在末尾加上結束標誌‘\0‘,換言之,n代表了字符數組的長度,即sizeof(str)。如果讀取過程中遇到換行符或文件結束標誌,讀取操作結束。若正常讀取,返回指向str代表字符串的指針,否則,返回NULL(空指針)。

技術分享圖片

C語言復習:文件操作