1. 程式人生 > >利用標準C語言庫函式進行文字檔案讀寫

利用標準C語言庫函式進行文字檔案讀寫

    利用C語言進行檔案操作的方法有多種。其中包括在UNIX系統環境下利用系統介面進行檔案操作;在windows系統下可以利用windows系統下可以利用fopen_s等庫函式的安全版本進行檔案操作。但是用的最多的就是利用標準庫函式進行檔案操作。本文主要介紹利用C標準庫函式進行檔案的開啟、讀取(一次一個字元、一次一行、格式化讀)、寫入(一次寫入一個字元、一次寫一行、格式化寫)和關閉操作。其中這些庫函式都包含在標頭檔案<stdio.h>中。

   啟動一個C語言程式時,作業系統環境會開啟標準輸入、標準輸出和標準錯誤3個檔案,相應的檔案指標分別為stdin、stdout和stderr(檔案指標stdin/stdout都是FILE*型別的物件,但他們是常量而非變數,不能對它們賦值)。在大多數環境中,stdin指向鍵盤,而stdout和stderr指向顯示器。當然也可以被重定向到檔案或者管道。

一、檔案開啟

1、fopen

1)函式原型:

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

2)引數

第一個引數是一個字串,它包含檔名。第二個引數也是字串,表示的是訪問模式。允許的模式包括:

“r”

如果檔案不存在,開啟失敗

"w"

如果檔案存在,把檔案截斷至0長;如果檔案不存在,會建立檔案

"a"

追加

如果檔案存在,在檔案尾進行寫;如果檔案不存在會建立檔案

"r+"

讀和寫

如果檔案不存在,開啟失敗

"w+"

讀和寫

如果檔案存在,把檔案截斷至0長;如果檔案不存在,會建立檔案

"a+"

讀和追加

如果檔案存在,在檔案尾進行寫;如果檔案不存在會建立檔案

預設的是操作文字檔案;如果在以上的模式中加上"b"例如:“rb”,"wb","ab+"等則表示操作二進位制格式的檔案。但是因為UNIX核心並不對這兩種檔案進行區分因此在UNIX系統環境下指定字元"b"作為模式的一部分並無意義。

3)返回值

若開啟成功則返回一個指向FILE物件的指標;若開啟失敗則返回NULL。

(FILE通常是一個結構,它包含了標準I/O庫為管理該流需要的所有資訊,包括用於實際I/O的檔案描述符、指向用於該流緩衝區的指標、緩衝區的長度、當前緩衝區的字元數以及出錯標誌等。

 但是在本例中FILE像int一樣是一個型別名,而不是結構標記)

4)示例

#include <stdio.h>
FILE *stream, *stream2;
void main( void )
{
   int numclosed;
   /* Open for read (will fail if file "data" does not exist) */
   if( (stream  = fopen( "data", "r" )) == NULL )
      printf( "The file 'data' was not opened\n" );
   else
      printf( "The file 'data' was opened\n" );
   /* Open for write */
   if( (stream2 = fopen( "data2", "w+" )) == NULL )
      printf( "The file 'data2' was not opened\n" );
   else
      printf( "The file 'data2' was opened\n" );
   /* Close stream */
   if(fclose( stream2 ))
	 printf( "The file 'data2' was not closed\n" );
   /* All other files are closed: */
   numclosed = _fcloseall( );
   printf( "Number of files closed by _fcloseall: %u\n", numclosed );
}
執行結果:



二、讀檔案(讀流)

1、一次讀一個字元

(1)fgetc

1)函式原型

int fgetc( 
   FILE* stream 
);

2)返回值

若成功返回下一個字元,若已到達檔案尾端或者出錯返回EOF;

①返回下一個字元時,將其unsigned char 型別轉換為int型別。返回整型的原因是可能會需要返回已出錯或已達檔案尾端的指示值EOF(通常為-1)。

②不管出錯還是到達尾端返回的都是EOF。為了區分這兩種情況,可以呼叫 int ferror(FILE *fp)或者int feof(FILE *fp)來進行判斷(若是真返回非0,否則返回0)。

3)示例

#include <stdio.h>
#include <stdlib.h>

void main( void )
{
   FILE *stream;
   char buffer[81];
   int  i, ch;

   /* Open file to read line from: */
   if( (stream = fopen( "fgetc.c", "r" )) == NULL )
      exit( 0 );

   /* Read in first 80 characters and place them in "buffer": */
   ch = fgetc( stream );
   for( i=0; (i < 80 ) && ( feof( stream ) == 0 ); i++ )
   {
      buffer[i] = (char)ch;
      ch = fgetc( stream );
   }

   /* Add null to end string */
   buffer[i] = '\0';
   printf( "%s\n", buffer );
   fclose( stream );
}

(2)getc

int getc( 
   FILE *stream 
);
getc()與fgetc()使用方法相同。區別是getc可被實現為巨集,而fgetc不能實現為巨集。

因此:①getc的引數不應該是有副作用的表示式,因為可能會被計算多次 ;

            ②fgetc是函式,因此可以得到其地址 ;

            ③fgetc的所需時間可能會比getc要長。

(3)getchar

int getchar(
    void 
);
getchar用來從標準輸入中讀取資料;被實現為巨集而不是函式。例如:
#define getchar() getc(stdin)

2、一次讀一行

(1)fgets

1)函式原型

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

string:讀取後存放的緩衝區地址;n:緩衝區長度;stream:要讀的檔案指標

3)返回值

若到達檔案尾或者出錯返回NULL;

若讀取成功返回讀取的字串。其中以行為單位進行讀取,讀入的位元組放入緩衝區。若一行中包括最後一個換行符的字元數大於n-1個;則fgets值返回不完整的行,對fgets的下次呼叫會繼續讀該行。緩衝區總是以NULL位元組結尾。

4)示例

#include <stdio.h>

void main( void )
{
   FILE *stream;
   char line[5];

   if( (stream = fopen( "fgets.c", "r" )) != NULL )
   {
	   while(fgets(line,5,stream)!=NULL)
	   {
         printf( "%s", line);
	   }
	   printf("\n");
      fclose( stream );
   }
}
輸出(與fgets.c中的檔案格式相同):


若只執行一次fgets輸出的是:/* f
(2)gets

char *gets( 
   char* buffer 
);
gets從標準輸入讀,但是不推薦使用該函式。因為gets沒有指定緩衝區長度,這樣如果一行的長度大於緩衝區的長度就有可能照成緩衝區溢位。

gets與fgets的另一個區別是gets並不將換行符存入緩衝區。

3、格式化讀

(1)fscanf

1)函式原型

int fscanf( 
   FILE* stream, 
   const char* format [, argument ]... 
);
2)引數

format:控制如何轉換引數,以便對它們賦值。格式與scanf中的格式控制相同。

argument:包含各變數的地址,用轉換結果對這些變數賦值。

3)返回值

返回賦值的輸入項數;若輸入錯誤(格式不匹配)或任意轉換前已經到達檔案尾端,返回EOF。

4)示例

#include <stdio.h>

FILE *stream;

void main( void )
{
   long l;
   float fp;
   char s[81];
   char c;

   stream = fopen( "fscanf.out", "w+" );
   if( stream == NULL )
      printf( "The file fscanf.out was not opened\n" );
   else
   {
      fprintf( stream, "%s %ld %f%c", "a-string", 
               65000, 3.14159, 'x' );

      /* Set pointer to beginning of file: */
      fseek( stream, 0L, SEEK_SET );

      /* Read data back from file: */
      fscanf( stream, "%s", s );
      fscanf( stream, "%ld", &l );

      fscanf( stream, "%f", &fp );
      fscanf( stream, "%c", &c );

      /* Output data read: */
      printf( "%s\n", s );
      printf( "%ld\n", l );
      printf( "%f\n", fp );
      printf( "%c\n", c );

      fclose( stream );
   }
}
輸出:


三、檔案寫

1、一次寫一個字元

(1)fputc

與輸入函式fgetc相對應

1)函式原型

int fputc( 
   int c, 
   FILE* stream 
);
2)返回值

若成功返回 c;若出錯返回EOF。
3)示例

/* FPUTC.C: This program uses fputc and _fputchar
 * to send a character array to stdout.
 */

#include <stdio.h>

void main( void )
{
   char strptr1[] = "This is a test of fputc!!\n";
   char strptr2[] = "This is a test of _fputchar!!\n";
   char *p;

   /* Print line to stream using fputc. */
   p = strptr1;
   while( (*p != '\0') && fputc( *(p++), stdout ) != EOF ) ;

   /* Print line to stream using _fputchar. */
   p = strptr2;
   while( (*p != '\0') && _fputchar( *(p++) ) != EOF )
      ;
}
(2)putc
int putc(
   int c,
   FILE *stream 
);
與getc相對應;被實現為巨集;fputc不能實現為巨集

示例:

/* This program uses putc to write buffer
 * to a stream. If an error occurs, the program
 * stops before writing the entire buffer.
 */

#include <stdio.h>

int main( void )
{
   FILE *stream;
   char *p, buffer[] = "This is the line of output\n";
   int  ch;

   ch = 0;
   /* Make standard out the stream and write to it. */
   stream = stdout;
   for( p = buffer; (ch != EOF) && (*p != '\0'); p++ )
      ch = putc( *p, stream );
}
 
(3)putchar

與getchar想對應;putchar(c)等同於putc(c,stdout);被實現為巨集

2、一次寫一行

(1)fputs

與fgets相對應

1)函式原型

int fputs( 
   const char* string, 
   FILE* stream 
);
2)返回值

若成功返回非負值,若失敗返回EOF

fputs將以NULL位元組終止的字串寫到指定的流;尾端的終止符NULL不寫出。注意:這並不是每次每次輸出一行,因為字串 不需要換行符作為最後一個非NULL位元組。

3)示例

/* FPUTS.C: This program uses fputs to write
 * a single line to the stdout stream.
 */

#include <stdio.h>

void main( void )
{
   fputs( "Hello world from fputs.\n", stdout );
}
(2)puts

與gets相對應。

int puts( 
   const char* string 
);
puts將一個以NULL位元組終止的字串寫到標準輸出,終止符不寫出。但是隨後會將一個換行符寫到標準輸出。

與gets不同puts是安全的。但是要注意最後新增的換行符。

3、格式輸出

(1)fprintf

與fscanf相對應

1)函式原型

int fprintf( 
   FILE* stream, 
   const char* format [, argument ]...
);
將資料格式化寫字stream流

2)示例

/* FPRINTF.C: This program uses fprintf to format various
 * data and print it to the file named FPRINTF.OUT. 
 */

#include <stdio.h>

FILE *stream;

void main( void )
{
   int    i = 10;
   double fp = 1.5;
   char   s[] = "this is a string";
   char   c = '\n';

   stream = fopen( "fprintf.out", "w" );
   fprintf( stream, "%s%c", s, c );
   fprintf( stream, "%d\n", i );
   fprintf( stream, "%f\n", fp );
   fclose( stream );
}

四、檔案關閉

1、fclose

(1)函式原型

int fclose( 
   FILE* stream 
);
(2)返回值

如果成功關閉返回0;若出錯返回EOF.

(3)說明

在檔案被關閉之前,會沖洗緩衝中的輸出資料。緩衝區中的任何輸入資料被丟棄。如果標準I/O庫已經為該流自動分配了一個緩衝區,則釋放此緩衝區。

2、_fcloseall

(1)函式原型

int _fcloseall( 
   void 
);
(2)返回值
如果成功則返回關閉的流的數目;失敗時返回EOF

(3)說明

函式會關閉除stdin、stdout以及stderr外的所有檔案流;其中包括由tepfile建立的臨時檔案。

參考資料:

1、MSDN

2、《UNIX環境高階程式設計》

3、《C程式設計語言》