Linux中的標准文件I/O流


1.fopen

表頭文件

include<stdio.h>

定義函數

FILE * fopen(const char * path,const char * mode);

函數說明

參數path字符串包含欲打開的文件路徑及文件名,參數mode字符串則代表着流形態。
mode有下列幾種形態字符串:
r 打開只讀文件,該文件必須存在。
r+ 打開可讀寫的文件,該文件必須存在。
w 打開只寫文件,若文件存在則文件長度清為0,即該文件內容會消失。若文件不存在則建立該文件。
w+ 打開可讀寫文件,若文件存在則文件長度清為零,即該文件內容會消失。若文件不存在則建立該文件。
a 以附加的方式打開只寫文件。若文件不存在,則會建立該文件,如果文件存在,寫入的數據會被加到文件尾,即文件原先的內容會被保留。
a+ 以附加方式打開可讀寫的文件。若文件不存在,則會建立該文件,如果文件存在,寫入的數據會被加到文件尾后,即文件原先的內容會被保留。
上述的形態字符串都可以再加一個b字符,如rb、w+b或ab+等組合,加入b 字符用來告訴函數庫打開的文件為二進制文件,而非純文字文件。不過在POSIX系統,包含Linux都會忽略該字符。由fopen()所建立的新文件會具有S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH(0666)權限,此文件權限也會參考umask值。

返回值

文件順利打開后,指向該流的文件指針就會被返回。若果文件打開失敗則返回NULL,並把錯誤代碼存在errno 中。

注意:

一般而言,開文件后會作一些文件讀取或寫入的動作,若開文件失敗,接下來的讀寫動作也無法順利進行,所以在fopen()后請作錯誤判斷及處理。


2.fread函數和fwrite函數

1.函數功能

用來讀寫一個數據塊。

2.一般調用形式

fread(buffer,size,count,fp);

fwrite(buffer,size,count,fp);

3.說明

(1)buffer:是一個指針,對fread來說,它是讀入數據的存放地址。對fwrite來說,是要輸出數據的地址。

(2)size:要讀寫的字節數;

(3)count:要進行讀寫多少個size字節的數據項;

(4)fp:文件型指針。

注意:1 完成一次寫操(fwrite())作后必須關閉流(fclose()),因為fwrite寫數據后,數據只是到了緩沖區,而沒有真正的寫在了文件里,等fclose執行后,fclose函數隱含包含了一次fflush操作,把緩沖區內的數據沖洗到終端;

2 完成一次讀操作(fread())后,如果沒有關閉流(fclose()),則指針(FILE * fp)自動向后移動前一次讀寫的長度,不關閉流繼續下一次讀操作則接着上次的輸出繼續輸出;fread()用來從文件流中讀取數據。參數fp為已打開的文件指針,參數ptr 指向欲存放讀取進來的數據空間,讀取的字符數以參數size*nmemb來決定。Fread()會返回實際讀取到的nmemb數目,如果此值比參數nmemb 來得小,則代表可能讀到了文件尾或有錯誤發生,這時必須用feof()或ferror()來決定發生什么情況。

fwrite()用來將數據寫入文件流中。參數fp為已打開的文件指針,參數buffer指向欲寫入的數據地址,總共寫入的字符數以參數size*nmemb來決定。Fwrite()會返回實際寫入的nmemb數目。


3 fprintf() :

#include <stdio.h>
int fprintf( FILE *stream, const char *format, ... );

函數功能:把格式化的數據寫入某個字符串
函數原型:int sprintf( char *buffer, const char *format [, argument] … );
返回值:字符串長度(strlen)

    char name[20] = "Mary";
    FILE *out;
    out = fopen( "output.txt", "w" );
    if( out != NULL )
    fprintf( out, "Hello %s\n", name );

格式化規定符

 
  %d 十進制有符號整數  
  %u 十進制無符號整數  
  %f 浮點數  
  %s 字符串  
  %c 單個字符  
  %p 指針的值  
  %e 指數形式的浮點數  
  %x, %X 無符號以十六進制表示的整數  
  %0 無符號以八進制表示的整數  
  %g 自動選擇合適的表示法  

按格式輸入到流,其原型是int fprintf(FILE *stream, const char *format[, argument, ...]);其用法和printf()相同,不過不是寫到控制台,而是寫到流罷了。注意的是返回值為此次操作寫入到文件的字節數。如int c = fprintf(fp, "%s %s %d %f", str1,str2, a, b) ;str1:10字節;str2: 10字節;a:2字節;b:8字節,c為33,因為寫入時不同的數據間自動加入一個空格。


4.fclose

函數原型:int fclose(FILE *stream)

功能:關閉一個文件流,使用fclose就可以把緩沖區內最后剩余的數據輸出到磁盤文件中,並釋放文件指針和有關的緩沖區
返回值

若關文件動作成功則返回0,有錯誤發生時則返回EOF並把錯誤代碼存到errno。


fopen /open區別

UNIX環境下的C 對二進制流文件的讀寫有兩套班子:1) fopen,fread,fwrite ; 2) open, read, write
這里簡單的介紹一下他們的區別。

  1. fopen 系列是標准的C庫函數;open系列是 POSIX 定義的,是UNIX系統里的system call。
    也就是說,fopen系列更具有可移植性;而open系列只能用在 POSIX 的操作系統上。
  2. 使用fopen 系列函數時要定義一個指代文件的對象,被稱為“文件句柄”(file handler),是一個結構體;而open系列使用的是一個被稱為“文件描述符” (file descriptor)的int型整數。
  3. fopen 系列是級別較高的I/O,讀寫時使用緩沖;而open系列相對低層,更接近操作系統,讀寫時沒有緩沖。由於能更多地與操作系統打交道,open系列可以訪問更改一些fopen系列無法訪問的信息,如查看文件的讀寫權限。這些額外的功能通常因系統而異。
  4. 使用fopen系列函數需要"#include <sdtio.h>";使用open系列函數需要"#include <fcntl.h>" ,鏈接時要之用libc(-lc)
    小結:
    總的來說,為了使程序獲得更好的可移植性,未到非得使用一些fopen系列無法實現的功能的情況下,fopen系列是首選。

read/write和fread/fwrite區別

1,fread是帶緩沖的,read不帶緩沖.
2,fopen是標准c里定義的,open是POSIX中定義的.
3,fread可以讀一個結構.read在linux/unix中讀二進制與普通文件沒有區別.
4,fopen不能指定要創建文件的權限.open可以指定權限.
5,fopen返回指針,open返回文件描述符(整數).
6,linux/unix中任何設備都是文件,都可以用open,read.
如果文件的大小是8k。
你如果用read/write,且只分配了2k的緩存,則要將此文件讀出需要做4次系統調用來實際從磁盤上讀出。
如果你用fread/fwrite,則系統自動分配緩存,則讀出此文件只要一次系統調用從磁盤上讀出。
也就是用read/write要讀4次磁盤,而用fread/fwrite則只要讀1次磁盤。效率比read/write要高4倍。
如果程序對內存有限制,則用read/write比較好。
都用fread 和fwrite,它自動分配緩存,速度會很快,比自己來做要簡單。如果要處理一些特殊的描述符,用read 和write,如套接口,管道之類的
系統調用write的效率取決於你buf的大小和你要寫入的總數量,如果buf太小,你進入內核空間的次數大增,效率就低下。而fwrite會替你做緩存,減少了實際出現的系統調用,所以效率比較高。
如果只調用一次(可能嗎?),這倆差不多,嚴格來說write要快一點點(因為實際上fwrite最后還是用了write做真正的寫入文件系統工作),但是這其中的差別無所謂。

fclose與close的區別

C FILE *流在內部使用緩沖I / O. fclose()刷新此緩沖區,然后在OS級別關閉文件描述符。close()'FILE *流可能無法刷新此內部緩沖區,您可能會丟失數據。因此對於C流總是使用C fxxx()函數
fclose函數隱含包含了一次fflush操作,把緩沖區內的數據沖洗到終端(因為標准I/O的操作對文件有緩沖,所以需要刷新緩沖區,close沒有這個操作,也沒有必要刷新的操作,因為對應的open,write,read沒有緩沖)


在標准IO中,對於文件的讀寫無非三種方式:

(1) 每次一個字符的I / O。
(2) 每次一行的I / O。
(3) 直接I / O。

似乎這些函數看起來很簡單,但只有理解了這些函數,你才知道該如何使用,例如怎樣來判斷讀取的文件是否結束,怎樣來統計文件的行數,等等。

下面先來回顧下讀取文件的三種方式,然后再舉例看下怎樣使用。

一次讀或寫一個字符,使用getc、fgetc或getchar。如果流是帶緩存的,則標准I / O函數處理所有緩存。三個函數原型如下:

        #include <stdio.h>
        int getc(FILE *fp) ;
        int fgetc(FILE *fp) ;
        int getchar(void);

三個函數的返回:若成功則為下一個字符,若已處文件尾端或出錯則為EOF。強調的是不管是出錯還是到達文件尾端,這三個函數都返回同樣的值。為了區分這兩種不同的情況,必須調用ferror()或feof()。

一次讀或寫一行,使用fgets和gets。兩個函數原型如下:

        #include <stdio.h>
        Char *gets(char *s) ;
        Char *fgets(char *s,int size,FILE *steam)

兩個函數返回:若成功則為buf,若已處文件尾端或出錯則為null。這兩個函數都指定了緩存地址,讀入的行將送入其中。gets()從標准輸入讀,而fgets()則從指定的流讀。

對於fgets(),必須指定緩存的長度為n。此函數一直讀到下一個新行符為止,但是不超過n-1個字符,讀入的字符被送入緩存,該緩存以null字符結尾。如若該行,包括后一個新行符的字符數超過n-1,則只返回一個不完整的行,而且緩存總是以null字符結尾。對fgets()的下一次調用會繼續讀該行。

直接I / O使用的是fread。每次I / O操作讀某種數量的對象,而每個對象具有指定的長度。這個函數常用於從二進制文件中讀一個結構。原型如下:
int fread(void * ptr,int objsize,int objnum,FILE *fp);

這個函數返回:讀的對象數。fputs()在遇到null字節時就停止,而在結構中可能含有null字節,所以不能使用每次一行函數實現這種要求,而fread能使我們一次讀整個結構。

下面看下如何利用上面提到的標准IO讀函數來判斷你讀取的文件是否結束以及如何來統計文件的行數。

1、判斷文件結束有三種方式:

a) 當用fgetc(src)返回的int值為EOF時,讀到文件的末尾
b) 當用fgets(p,1024,src)返回的指針為NULL時,讀到文件的末尾
c) 當用fread(s,1,20,src)返回的個數小於20時,讀到文件的末尾

實現起來簡單明了,就不再細說。

2、統計文件的行數

a) 當用fgetc(src)返回的int值為’\n’時行數加一。
b) 當用fgets(p,1024,src)返回的指針不為NULL時,你的緩沖即提供的p[1024]已經讀到字符,這個時候實際上有兩種情況,一種你讀進來的字符長度小於1024個,這個時候一行結束,行數加一,另一種比較復雜,由於受自己定義的緩沖的限制,一次只能讀進來1023個,如果恰好這個時候你剛好讀完一行,那p[]1022]里存放的一定’\n’,如果不是,那你的一行還一定沒有結束了。

程序如下:
Int n;
A) While(fgetc(src)’\n’)
N++;
B) while(fgets(p,1024,src)!=NULL)
{
If(strlen(p)<1024||p[1022]
’\n’)
N++;
}

標准I/O操作函數

1)打開文件

打開文件有3個標准函數,分別為fopen()、fdopen()和freopen()。它們可以不同的模式打開,但都返回一個指向FILE的指針,該指針指向對應的I/O流。此后,對文件的讀寫都是通過這個FILE指針來進行的。其中,fopen()函數可以指定打開文件的路徑和模式,fdopen()函數可以指定打開的文件描述符和模式,而freopen()函數除可指定打開的文件、模式外,還可指定特定的I/O流。

fopen()函數語法要點

其中,mode類似於open()函數中的flag,可以定義打開文件的訪問權限等,表2.17說明了fopen()中mode的各種取值。

mode取值說明


注意:在每個選項中加入b字符用來告訴函數庫打開的文件為二進制文件,而非純文本文件。不過在Linux系統中會自動識別不同類型的文件而將此符號忽略。

fdopen()函數語法要點

freopen()函數語法要點

2)關閉文件

關閉標准流文件的函數為fclose(),該函數將緩沖區內的數據全部寫入到文件中,並釋放系統所提供的文件資源。

fclose()函數語法要點

3)讀文件

在文件流被打開后,可對文件流進行讀寫等操作,其中,讀操作的函數為fread()。

fread()函數語法要點

4)寫文件

fwrite()函數用於對指定的文件流進行寫操作。
fwrite()函數語法要點

文件打開之后,根據一次讀寫文件中字符的數目可分為字符輸入/輸出、行輸入/輸出和格式化輸入/輸出,下面分別對這3種不同的方式進行討論。

1)字符輸入/輸出

字符輸入/輸出函數一次僅讀寫一個字符。其中字符輸入/輸出函數的語法如下表所示:

字符輸入函數語法

字符輸出函數語法

這幾個函數功能類似,其區別僅在於getc()和putc()通常被實現為宏,而fgetc()和fputc()不能實現為宏,因此,函數的實現時間會有所差別。
下面這個實例結合fputc()和fgetc(),將標准輸入復制到標准輸出中。

 /*fput.c*/
    #include<stdio.h>
    main()
    {
        int c;
        /* 把fgetc()的結果作為fputc()的輸入 */
        fputc(fgetc(stdin), stdout);
    }
輸出結果:
  $ ./fput
    w(用戶輸入)
    w(屏幕輸出)

2)行輸入/輸出

行輸入/輸出函數一次操作一行,其中行輸入/輸出函數語法要點如下表所示:

行輸入函數語法

行輸出函數語法

這里以gets()和puts()為例進行說明,本實例將標准輸入復制到標准輸出中,如下所示:

    /*gets.c*/
    #include<stdio.h>
    main()
    {
        char s[80];
        fputs(fgets(s, 80, stdin), stdout);
    }

    運行該程序,結果如下所示:

    $ ./gets
    This is stdin(用戶輸入)
    This is stdin(屏幕輸出)

3)格式化輸入/輸出

格式化輸入/輸出函數可以指定輸入/輸出的具體格式,這里有讀者已經非常熟悉的printf()、scanf()等函數,這里簡要介紹一下它們的格式,如下表所示:

格式化輸出函數1

格式化輸出函數2

格式化輸入函數


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM