C檔案IO

來源:互聯網
上載者:User

標籤:io   c   

ANSI C標準幾乎被所有的作業系統支援,ANSI C標準提供了完善的I/O函數,使用這些I/O操作我們可以控製程序的輸入輸出、讀寫系統磁碟檔案。本文記錄了使用者進程I/O緩衝介紹、檔案的讀寫、檔案定位操作等內容。

庫函數與系統調用

檔案是位於磁碟上的,如何在啟動並執行程式(進程)中控制檔案的讀寫,通過下面的這張圖,我們可以看到應用程式如何控制系統資源(包括磁碟中的檔案)的大概的原理。


作業系統協助我們管理硬體資源,封裝底層實現,以介面的形式(系統調用函數)供上層應用程式調用。直接使用作業系統提供的系統調用函數我們就可以控制檔案的讀寫,但是一般我們在應用程式中不提倡這樣做,因為會帶來效能上的問題。應用程式調用系統調用函數的時候,作業系統會從使用者態轉變成核心態,完成調用後,再從核心態轉成使用者態(Linux中是通過非強制中斷實現的),而頻繁的系統狀態切換是需要開銷的,會給使用者應用程式帶來效能上的問題。另外就是可移植性的問題,每個作業系統向外提供的介面是不盡相同的,如果直接在應用程式中使用系統調用函數,那麼程式的可移植性將會很差。在ANSI C標準中為我們提供了標準函數(庫函數)去作業系統調用函數,庫函數是一些能夠完成特定功能的函數,一般由某個標準組織發布,並形成一種公認的標準,所以使用庫函數可以屏蔽作業系統間對外介面的差異。

下面是網友總結的庫函數和系統調用函數之間的差異


緩衝和非緩衝

對於檔案的訪問操作,可以按照是否使用緩衝區,分為緩衝檔案操作和非緩衝檔案操作

緩衝檔案操作:進階檔案操作,如第一幅圖使用者應用程式的左分支,在使用者進程空間為開啟的檔案分配緩衝區。ANSI C標準就是使用的這種檔案操作。本文討論的正是這種。

非緩衝檔案操作:低級檔案操作,如第一幅圖使用者應用程式的右分支,在使用者進程空間不設檔案緩衝區。POSIX C標準的I/O就是使用的非緩衝檔案操作。

說明:這裡所說的緩衝全部指的是使用者進程空間的檔案緩衝,即使是非緩衝的檔案操作,在核心部分也是使用了多級緩衝,而不是直接存取磁碟(可以參考作業系統的儲存空間結構)。


檔案與檔案流

流是一種抽象的資料結構,是ANSI C用來高效的管理開啟了的檔案資訊的,在實際編程中的體現就是struct FILE結構體,在stdio.h標頭檔中定義,流對象最重要的機制就是緩衝區和格式轉換。在Linux系統中系統預設為每個進程開啟3個檔案,對應3個流就是標準輸入資料流、標準輸出資料流、標準錯誤流。除此之外,需要用到的其他檔案需要自己開啟和關閉。


檔案的I/O操作

按照讀/寫的對象、可以分為按字元、行、塊、格式化等幾種讀寫操作。


(1)按字元讀/寫檔案流

下面的這段程式是使用fgetc和fputc來讀取指定檔案中的內容到標準輸出(顯示器),運行時需要指定一個檔案

#include<stdio.h>int main(int argc,char *argv[]){    FILE *fp=NULL;    char ch;    if(argc<=1)     {        printf("check usage of %s \n",argv[0]);        return -1;    }    if((fp=fopen(argv[1],"r"))==NULL)     {        printf("can not open %s\n",argv[1]);        return -1;    }    while ((ch=fgetc(fp))!=EOF)         fputc(ch,stdout);     fclose(fp);     return 0;}

運行程式:



(2)按行讀/寫檔案流

下面是一個使用fgets和fputs實現上述功能的程式

#include<stdio.h>int main(int argc,char *argv[]){    FILE *fp=NULL;    char str[20];    if((fp=fopen(argv[1],"r"))==NULL)            //按唯讀形式開啟檔案    {        printf("can not open!\n");        return -1;    }    fgets(str,sizeof(str),fp);                          //從開啟檔案中讀取sizeof(str)個位元組到str中    fputs(str,stdout);                                 //將str輸出到標準輸出    fclose(fp);                                          //關閉檔案    return 0;}

(3)按照塊讀寫

下面是使用fread和fwrite來實現的按塊讀寫的程式

#include<stdio.h>int main(int argc,char *argv[]){    struct student    {        char name[10];        int number;    };    FILE *fp=NULL;    int i;    struct student boya[2],boyb[2],*pp,*qq;    if((fp=fopen("aa.txt","w+"))==NULL) //以可讀寫的方式開啟檔案;若該檔案存在則清空,若不存在就建立    {  //開啟檔案失敗        printf("can not open!\n");        return -1;    }    pp=boya;    qq=boyb;    printf(“please input two students‘ name and number:\n");    for (i=0;i<2;i++,pp++)     scanf("%s\%d",pp->name,&pp->number);    pp=boya;    fwrite(pp,sizeof(struct student),2,fp); //將從鍵盤輸入的資訊寫入到檔案流fp中    rewind(fp); //將讀寫位置定位到檔案頭    fread(qq,sizeof(struct student),2,fp); //從檔案流fp中讀兩個結構體到qq    printf("name\t\t number\n");    for(i=0;i<2;i++,qq++) //輸出qq中的內容        printf("%s\t\t %d\n",qq->name,qq->number);    fclose(fp);    return 0;}

(4)按照格式化讀/寫

下面這段程式是使用sprintf和sscanf進行檔案讀/寫操作

#include<stdio.h>int main(){        FILE *fp = NULL;        int i = 20;        char ch = 'D';        if((fp=fopen("f2","r+"))==NULL)        {                printf("open file f2 failed.");                return -1;        }        fprintf(fp,"%d:%c\n",i,ch);  //按照指定格式寫檔案        int new_i = 0;        char new_ch;        rewind(fp);  //使讀寫指標歸位        fscanf(fp,"%d:%c\n",&new_i,&new_ch);  //按照指定格式讀檔案        printf("new_i=%d, new_ch=%c\n",new_i,new_ch);        fclose(fp);        return 0;}

程式運行結果:



再說緩衝區

可以發現上述4中形式的讀寫操作,都沒有指明緩衝區,但是它們都使用到了位於使用者進程空間的檔案緩衝區,這是因為,對於任意的流,如果沒有指明其緩衝區的類型,系統將指定預設類型的緩衝區。如果使用者希望自己指定緩衝區,可以使用setbuf( )或者setvbuf( )函數指定,這兩個函數的聲明如下:

extern  void  setbuf ( 流對象, 緩衝區);

如果將緩衝區設定為NULL,則關閉緩衝區。

extern  int  setvbuf (流對象,緩衝區, 模式,緩衝區大小)

setvbuf比setbuf更加靈活,其中模式可取值有0、1、2,分別表示全緩衝、行緩衝、無緩衝,如果模式是2,那麼將會忽視第二和第四個參數。

下面是一個修改緩衝區的程式:

/* Example show usage of setbuf() &setvbuf() */#include<stdio.h>#include<error.h>#include<string.h>int main( int argc , char ** argv ){int i;FILE * fp;char msg1[]="hello,wolrd\n";char msg2[] = "hello\nworld";char buf[128];//open a file and set nobuf(used setbuf).and write string to it,check it before close of flush the streamif(( fp = fopen("no_buf1.txt","w")) == NULL){perror("file open failure!");return(-1);}setbuf(fp,NULL);memset(buf,'\0',128);fwrite( msg1 , 7 , 1 , fp );printf("test setbuf(no buf)!check no_buf1.txt\n");printf("now buf data is :buf=%s\n",buf);printf("press enter to continue!\n");getchar();fclose(fp);//open a file and set nobuf(used setvbuf).and write string to it,check it before close of flush the streamif(( fp = fopen("no_buf2.txt","w")) == NULL){perror("file open failure!");return(-1);}setvbuf( fp , NULL, _IONBF , 0 );memset(buf,'\0',128);fwrite( msg1 , 7 , 1 , fp );printf("test setvbuf(no buf)!check no_buf2.txt\n");printf("now buf data is :buf=%s\n",buf);printf("press enter to continue!\n");getchar();fclose(fp);//open a file and set line buf(used setvbuf).and write string(include '\n') to it,////check it before close of flush the streamif(( fp = fopen("l_buf.txt","w")) == NULL){perror("file open failure!");return(-1);}setvbuf( fp , buf , _IOLBF , sizeof(buf) );memset(buf,'\0',128);fwrite( msg2 , sizeof(msg2) , 1 , fp );printf("test setvbuf(line buf)!check l_buf.txt, because line buf ,only data before enter send to file\n");printf("now buf data is :buf=%s\n",buf);printf("press enter to continue!\n");getchar();fclose(fp);//open a file and set full buf(used setvbuf).and write string to it for 20th time (it is large than the buf)//check it before close of flush the streamif(( fp = fopen("f_buf.txt","w")) == NULL){perror("file open failure!");return(-1);}setvbuf( fp , buf , _IOFBF , sizeof(buf) );memset(buf,'\0',128);fwrite( msg2 , sizeof(msg2) , 1 , fp );printf("test setbuf(full buf)!check f_buf.txt\n");printf("now buf data is :buf=%s\n",buf);printf("press enter to continue!\n");getchar();fclose(fp);}


其他檔案操作

下面介紹一些在檔案操作中常用的函數

開啟、關閉檔案操作fopen和fclose

fp = fopen(檔案名稱,檔案操作方式);

檔案的操作方式有以下這些


關閉檔案使用fclose(檔案指標); 或者 fcloseall()函數。


檔案流檢測

extern  int  feof(流對象)

用於判斷流對象是否讀到檔案尾部,如果是返回1,否則,返回0;


extern  int   ferror(流對象) 

用於判斷流對象是否出現了錯誤,若沒有錯誤,則返回0,否則,返回非0。


extern  long  int  ftell(流對象)

返回當前讀寫位置距離檔案開頭位置的位元組數,若執行失敗,返回-1


extern  int  fseek(流對象,位移距離,基準位置)

基準位置可取值有0、1、2分別表示檔案開頭、當前位置、檔案結尾

將讀寫位置移到,距離基準位置位移距離處。若成功,返回0;否則,返回,-1


extern  void  rewind(流對象)

將讀寫位置重設到檔案開始處。


C檔案IO

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.