Linux 程式設計學習筆記----ANSI C 檔案I/O管理

來源:互聯網
上載者:User

標籤:des   style   blog   http   使用   os   strong   檔案   

轉載請註明出處:http://blog.csdn.net/suool/article/details/38129201


問題引入

檔案的種類

根據資料存放區的方式不同,可以將檔案分為文字檔和二進位檔案.具體的區別和關係如下:

文字檔與二進位檔案在電腦檔案系統中的實體儲存體都是二進位的,也就是在實體儲存體方面沒有區別都是01碼,這個沒有異議,他們的區別主要在邏輯儲存上,也就是編碼上。
文字檔格式儲存時是將值作為字元然後存入其字元編碼的二進位文字檔用‘字元’作為單位來表示和儲存資料,比如對於1這個值,文字檔會將其看做字元‘1’然後儲存其ASCII編碼值(這裡假定是ASCII編碼),這樣在物理上就是0x31這個二進位值,而若是二進位儲存1,則直接儲存其二進位值,比如如果程式中是處理1為整數則儲存的二進位值就是 0x00000001 (4位元組)。
當然如果程式本來就是按字元儲存的 也就是 char ch =‘1‘ ;則二進位儲存後值就是其ASCII碼,因為該變數的二進位本來就是其ASCII碼。可以總結出二進位檔案就是值本身的編碼,那麼就是不定長的編碼了,因為值本身就是不等位元組的,如整數4個位元組那麼儲存在二進位檔案就是這四個位元組的原生二進位值。

綜上,可以知道文字檔與二進位檔案就是編碼方式不一樣而已,而這個是使用者行為,把一個資料以什麼樣的編碼(字元還是值本身)存入檔案是由使用者主動選擇的,也就是寫入的介面選擇,如果以二進位介面方式寫入檔案那麼就是一個二進位檔案,如果以字元方式寫入檔案就是一個文字檔了。既然有寫入時候的編碼也就會有讀出的編碼,只有兩個編碼對應才能讀出正確的結果,如用記事本開啟一個二進位檔案會呈現亂碼的,這裡稍微提一下尾碼名,尾碼名並不能確定其是否就是文字檔,二進位檔案也可以是txt尾碼名,尾碼名只是用來關聯開啟程式,給使用者做備忘用的,與檔案的具體編碼沒有關係

可以使用字元介面讀寫二進位檔案,只需要做些處理即可,所以所謂的二進位檔案,文字檔主要體現在讀寫方式這裡。此外windows有一個明顯的區別是對待文字檔讀寫的時候,會將換行 \n自動替換成 \r\n。

最後文字檔和二進位檔案主要是windows下的概念,UNIX/Linux並沒有區分這兩種檔案,他們對所有檔案一視同仁,將所有檔案都看成二進位檔案。

檔案操作方式

根據應用程式對檔案的訪問方式不同,可以分為帶緩衝區的檔案操作和非緩衝檔案操作,具體區別簡單介紹如下:

緩衝檔案操作:進階檔案操作,將在使用者空間中自動為正在使用的檔案開闢記憶體緩衝區,遵循ANSI C標準的I/O函數就是緩衝檔案操作

非緩衝檔案操作:低級檔案操作,如果需要,只能由使用者自己在程式中為每個檔案設定緩衝區,遵循POSIX標準的系統調用I/O函數就是這種類型.

具體的區別見:http://www.360doc.com/content/11/0521/11/5455634_118306098.shtml


ANSI C庫函數為了實現他的特性,採用了流的概念,在流的實現中,緩衝區是最重要的單元,根據需要可以分為全緩衝,行緩衝,無緩衝.

下面就具體講一下關於ANSI  C的流和檔案I/O相關的方法和函數.

問題解析關於流及其功能

在linux系統中,系統預設為每個進程開啟了三個檔案,即是每個進程可以預設操作三個流:標準輸入資料流,輸出資料流,錯誤流.

在系統源檔案中的宏定義如下:


檔案流的主要功能是:

1‘格式化內容:實現不同輸入輸出格式轉換.

2‘緩衝功能:將資料讀寫集中,從而減少系統調用次數

檔案流指標

在應用編程層面上,程式對流的操作主要是體現在檔案流指標FILE上,操作一個檔案之前,需要使用fopen開啟檔案,之後返回該檔案的檔案流指標與該檔案關聯,所有針對該檔案的讀寫操作都是通過檔案指標完成.下面是在應用程式層能夠訪問的FILE的結構體,因此結構體成員可以在使用者空間訪問:

struct _IO_FILE {  int _flags;           /* High-order word is _IO_MAGIC; rest is flags. */#define _IO_file_flags _flags  /* The following pointers correspond to the C++ streambuf protocol. */  /* Note:  Tk uses the _IO_read_ptr and _IO_read_end fields directly. */  char* _IO_read_ptr;   /* Current read pointer */  char* _IO_read_end;   /* End of get area. */  char* _IO_read_base;  /* Start of putback+get area. */  char* _IO_write_base; /* Start of put area. */  char* _IO_write_ptr;  /* Current put pointer. */  char* _IO_write_end;  /* End of put area. */  char* _IO_buf_base;   /* Start of reserve area. */  char* _IO_buf_end;    /* End of reserve area. */..............  int _fileno;                         / 檔案描述符#if 0}
上面那個結構體中包含了I/O庫為管理該流所需要的所有資訊.

下面是列印了一個開啟的檔案流指標指標成員資訊的樣本程式,在此程式中,使用標準的C庫函數實現了檔案的複製操作,在複製過程中,即時列印當前的讀寫位置和緩衝區資訊.

代碼如下:

#include <stdlib.h>#include <stdio.h>#include <string.h>#define prt(CONTENT,MSG) printf(CONTENT":\t%p\n",MSG)int main(int argc, char *argv[]){    FILE *fp_src,*fp_des;                                // 檔案讀寫指標     char buffer[10],buffer1[128];                        // 檔案讀寫緩衝區    int i;    if((fp_src=fopen(argv[1],"r+"))== NULL)             // 從第一個命令列檔案參數,讀檔案    {        perror("open1");        exit(EXIT_FAILURE);    }    if((fp_des=fopen(argv[2],"w+"))== NULL)              // 從第二個命令列檔案參數,寫檔案    {        perror("open2");        exit(EXIT_FAILURE);    }    setvbuf(fp_src,buffer1,_IOLBF,128);                  // 顯式設定緩衝區的位置和類型    do    {        /*         * 以下的prt內容均是屬於提示性內容,是讀取應用程式層所能訪問的FILE結構體得到的.         */        prt("src_IO_read_ptr",fp_src->_IO_read_ptr);     // 源檔案讀位置,source        prt("_IO_read_end",fp_src->_IO_read_end);        prt("_IO_read_base",fp_src->_IO_read_base);        prt("src_IO_write_ptr",fp_src->_IO_write_ptr);        prt("_IO_write_base",fp_src->_IO_write_base);        prt("_IO_write_end",fp_src->_IO_write_end);        prt("_IO_buf_base",fp_src->_IO_buf_base);        // 源檔案緩衝區位置        prt("_IO_buf_end",fp_src->_IO_buf_end);        memset(buffer,'\0',10);        i = fread(buffer,1,10,fp_src);                   // 讀        fwrite(buffer,1,i,fp_des);                       // 寫        prt("des_IO_read_ptr",fp_des->_IO_read_ptr);          prt("des_IO_write_ptr",fp_des->_IO_write_ptr);   // 目標檔案寫位置    }while(i==10);    fclose(fp_src);    fclose(fp_des);                                      // 關閉檔案讀寫}

運行結果如下:


最終成功複製.

緩衝區類型

剛剛說了三種緩衝區類型,他們依然在系統原始碼檔案有所定義.

對於標準流ANSI C有如下的要求:

1.標準輸入輸出:若且唯若不涉及互動作用裝置時,他們才是全緩衝的

2.標準錯誤:絕不會是全緩衝的

下面是用於測試緩衝區類型的樣本程式.

代碼:

#include <stdio.h>void pr_stdio(const char *, FILE *);int main(void){    FILE    *fp;    fputs("enter any character\n", stdout);    if(getchar()==EOF)        printf("getchar error");    fputs("one line to standard error\n", stderr);    pr_stdio("stdin",  stdin);                    // test for standard input stream    pr_stdio("stdout", stdout);                   // test for standard output stream    pr_stdio("stderr", stderr);                   // test for standard error stream    if ( (fp = fopen("/etc/motd", "r")) == NULL)  // 普通檔案        printf("fopen error");    if (fgetc(fp) == EOF)        printf("getc error");    pr_stdio("/etc/motd", fp);    return(0);}void pr_stdio(const char *name, FILE *fp){    printf("stream = %s, ", name);     if (fp->_flags & _IO_UNBUFFERED)              // 無緩衝        printf("unbuffered");    else if (fp->_flags & _IO_LINE_BUF)           // 行緩衝        printf("line buffered");    else        printf("fully buffered");                 // 全緩衝    printf(", buffer size = %ld\n", fp->_IO_buf_end-fp->_IO_buf_base);}

結果如下:

指定緩衝區類型,通過sretbuf函數.其中三種緩衝的定義如下:


一個範例程式碼如下:

/* 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 stream    if(( fp = fopen("no_buf1.txt","w")) == NULL)    {        perror("file open failure!");        return(-1);    }    setbuf(fp,NULL);                                    // set the buff NULL    memset(buf,'\0',128);    fwrite( msg1 , 7 , 1 , fp );                        // write message to file    printf("test setbuf(no buf)!check no_buf1.txt\n");    printf("now buf data is :buf=%s\n",buf);            // test buff    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 stream    if(( fp = fopen("no_buf2.txt","w")) == NULL)    {        perror("file open failure!");        return(-1);    }    setvbuf( fp , NULL, _IONBF , 0 );    memset(buf,'\0',128);                                // clear the buff    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 stream    if(( 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 stream    if(( 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);    // check data of the current buff     printf("press enter to continue!\n");    getchar();    fclose(fp);}

result:


ANSI C 檔案I/O操作

1.開啟關閉檔案三個函數:

fopen()

fclose()

fflush()  // 重新整理流

具體代碼見stdio.h原始碼

2.讀寫檔案

字元單位

1.字元讀

從流中讀取


從stdin中:

/* Read a character from stdin.   This function is a possible cancellation point and therefore not   marked with __THROW.  */extern int getchar (void);
2.字元寫

/* Write a character to STREAM.   These functions are possible cancellation points and therefore not   marked with __THROW.   These functions is a possible cancellation point and therefore not   marked with __THROW.  */extern int fputc (int __c, FILE *__stream);extern int putc (int __c, FILE *__stream);/* Write a character to stdout.   This function is a possible cancellation point and therefore not   marked with __THROW.  */extern int putchar (int __c);
行單位

行讀出&行寫入

/* Write a string to STREAM.   This function is a possible cancellation point and therefore not   marked with __THROW.  */extern int fputs (const char *__restrict __s, FILE *__restrict __stream);/* Write a string, followed by a newline, to stdout.   This function is a possible cancellation point and therefore not   marked with __THROW.  */extern int puts (const char *__s);

塊單位

讀寫

/* Read chunks of generic data from STREAM.   This function is a possible cancellation point and therefore not   marked with __THROW.  */extern size_t fread (void *__restrict __ptr, size_t __size,     size_t __n, FILE *__restrict __stream) __wur;/* Write chunks of generic data to STREAM.   This function is a possible cancellation point and therefore not   marked with __THROW.  */extern size_t fwrite (const void *__restrict __ptr, size_t __size,      size_t __n, FILE *__restrict __s);

檔案流檢測

即是檢測檔案是否已經讀寫到末尾或者出錯.,使用feof函數.

對於ascii碼檔案,可以通過是否=EOF判斷

對於二進位檔案,則需要使用feof來判斷是否結束,結束返回1.否則返回0.

ferror函數判斷是否出錯,無錯返回0.

檔案流定位

ftell()函數返迴流的當前讀寫位置距離檔案開始的位元組數

fseek()函數修改當前讀寫位置

rewind()函數重設讀寫位置到開頭.

/* Seek to a certain position on STREAM.   This function is a possible cancellation point and therefore not   marked with __THROW.  */extern int fseek (FILE *__stream, long int __off, int __whence);/* Return the current position of STREAM.   This function is a possible cancellation point and therefore not   marked with __THROW.  */extern long int ftell (FILE *__stream) __wur;/* Rewind to the beginning of STREAM.   This function is a possible cancellation point and therefore not   marked with __THROW.  */extern void rewind (FILE *__stream);

執行個體應用

為實現磁碟檔案的複製,在ANSI C 的標準下,首先需要將源檔案和目標檔案建立聯絡以讀的方式開啟源檔案,以寫的方式開啟目標檔案,先將源檔案的資料集中寫到緩衝區,然後從記憶體寫入目標檔案所在的磁碟.

其中最重要的是判斷檔案的是否結束.一種方式是使用feof函數,另一種方式是使用讀操作的返回值,比如,每次讀128位元組,只有讀到結束位置的時候毒的位元組數會小於128,如果出錯則返回-1.

一種實現代碼如下:

#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)//以唯讀形式開啟argv[1]所指明的檔案    {        printf("can not open %s\n",argv[1]);        return -1;    }    while ((ch=fgetc(fp))!=EOF)   //把已開啟的檔案中的資料逐位元組的輸出到標準輸出stdout        fputc(ch,stdout);    fclose(fp);       //關閉檔案    return 0;}
具體結果就不示範了.可以自行編譯驗證.


ElSE

流的格式化輸入和輸出操作

其實就是幾個函數的講解,這個部分很多資料,所以不在講了,具體的可以自己google或者看原始碼了.

1.

printf()和scanf(0函數.

2.

fprintf()函數和fscanf()函數

3.sprintf函數

4.sscanf()函數

下面貼一下一個使用sscanf擷取cpu頻率的樣本程式:

#include <stdio.h> #include <string.h> float get_cpu_clock_speed () {       FILE* fp;       char buffer[1024];       size_t bytes_read;       char* match;       float clock_speed;              fp = fopen ("/proc/cpuinfo", "r");       bytes_read = fread (buffer, 1, sizeof (buffer), fp);       fclose (fp);       if (bytes_read == 0 || bytes_read == sizeof (buffer))       return 0;       buffer[bytes_read] = '\0';       match = strstr (buffer, "cpu MHz");//匹配       if (match == NULL)       return 0;       sscanf (match, "cpu MHz : %f", &clock_speed);//讀取       return clock_speed; } int main (void) {       printf ("CPU clock speed: %4.0f MHz\n", get_cpu_clock_speed ());       return 0; }

result:


reference

關於二進位和文字檔 http://www.cnblogs.com/whutzhou/p/3215210.html

linux進階程式設計 李宗德

NEXT

POSIX 檔案及目錄管理

普通檔案\串連檔案及目錄檔案屬性管理


轉載請註明出處:http://blog.csdn.net/suool/article/details/38129201

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.