1. 程式人生 > >C語言再學習 -- 檔案

C語言再學習 -- 檔案

檔案是什麼

一個檔案(file)通常就是磁碟上的一段命名的儲存區。C 將檔案看成是連續的位元組序列,其中每一個位元組都可以單獨地讀取。

二進位制和文字模式
1、在windows系統中,文字模式下,檔案以"\r\n"代表換行。若以文字模式開啟檔案,並用fputs等函式寫入換行符"\n"時,函式會自動在"\n"前面加上"\r"。即實際寫入檔案的是"\r\n" 。

2、在類Unix/Linux系統中文字模式下,檔案以"\n"代表換行。所以Linux系統中在文字模式和二進位制模式下並無區別。

標準檔案

C 程式自動開啟3個檔案。這3個檔案被稱為標準輸入,標準輸出和標準錯誤輸出。預設的標準輸入是系統的一般輸入裝置,通常為鍵盤;預設的標準輸出和標準錯誤輸出是系統的一般輸出裝置,通常為顯示器,分別得到檔案描述符 0, 1, 2.

下面的方法從標準輸入(鍵盤)獲得一個字元:  ch = getchar ( );

標準檔案指標:

stdio.h檔案把3個檔案指標與3個C 程式自動開啟的標準檔案進行了並聯,如下表所示:

標準檔案  

檔案指標  

一般使用的裝置  

標準輸入

stdin

鍵盤

標準輸出

stdout

顯示器

標準錯誤

stderr

顯示器

這些指標都是FILE指標型別,所以可以被用作標準I/O函式的引數。

stdout和stderr比較:

stderr -- 標準錯誤輸出裝置
stdout -- 標準輸出裝置 (printf("..")) 同 stdout。
兩者預設向螢幕輸出。但如果用轉向標準輸出到磁碟檔案,則可看出兩者區別。stdout輸出到磁碟檔案,stderr在螢幕

,例如:
fprintf(stderr, "Can't open it!\n");
fprintf(stdout, "Can't open it!\n");
在my.exe
Can't open it!
Can't open it!
Can't open it!

轉向標準輸出到磁碟檔案tmp.txt
my.exe > tmp.txt
Can't open it!

用TYPE 看 tmp.txt的內容:
TYPE tmp.txt
Can't open it!
Can't open it!

stderr是不快取的,stdout是行間快取的。請注意:
for(i = 0; i < 10; i++)
    {
      fprintf(stdout, "This is stdout[%d]", i);
      fprintf(stderr, "This is stderr[%d]", i);
    }
會全部顯示stderr之後,再顯示stdout。又因為stdout是行內快取,所以加 \n 後會立刻顯示。

檔案操作分成如下三個步驟:

1、開啟檔案 (fopen)

2、操作檔案 (fread/fwrite)

3、關閉檔案 (fclose)

下面來一一介紹:

開啟檔案 -- fopen ( )函式:

函式原型:
FILE * fopen(const char * path,const char * mode);

返回值:

檔案順利開啟後,指向該流的檔案指標就會被返回。如果檔案開啟失敗則返回NULL,並把錯誤程式碼存在errno中。
一般而言,開啟檔案後會做一些檔案讀取或寫入的動作,若開啟檔案失敗,接下來的讀寫動作也無法順利進行,所以一般在fopen()後作錯誤判斷及處理

引數說明:

path:字串包含欲開啟的檔案路徑及檔名

mode:C 字串,包含了檔案訪問模式,模式如下:

模式字串

“r”

以只讀方式開啟檔案,該檔案必須存在

“r+”

以只讀寫方式開啟檔案,該檔案必須存在

“w”

開啟只寫檔案,若檔案存在則檔案長度清零,即該檔案內容會消失。

若檔案不存在則建立該檔案

“w+”

開啟可讀寫檔案,若檔案存在則檔案長度清零,即該檔案內容會消失。

若檔案不存在則建立該檔案。

“a”

以附加的方式開啟只寫檔案。若檔案不存在,則會建立該檔案,如果檔案存在,

寫入的資料會被加到檔案尾,即檔案原先的內容會被保留。(EOF符保留)

“a+”

以附加方式開啟可讀寫的檔案。若檔案不存在,則會建立檔案,如果檔案存在,

寫入的資料會被加到檔案尾後,即檔案原先的內容會被保留。(原來的EOF符不保留)

“rb”, “wb”, “ab”, “ab+”, “a+b”,

 “wb+”, “w+b”, “ab+”, “a+b”

與前面的模式相似,只是使用二進位制模式而非文字模式開啟檔案

關閉檔案 -- fclose ( )函式:

函式原型:

int fclose( FILE *fp );

返回值:
如果流成功關閉,fclose 返回 0,否則返回EOF(-1)。(如果流為NULL,而且程式可以繼續執行,fclose設定error number給EINVAL,並返回EOF。)

因此,可在fclose(fp)後使用
if(fclose())
{
    perror("fclose");
}
來判斷是否成功關閉檔案,關閉失敗,則fclose返回“1”並輸出出錯原因。

  1. 示例一:  
  2. #include<stdio.h>
  3. int main(void)  
  4. {  
  5.     FILE*fp = NULL;  
  6.     fp = fopen("abc.txt""r");  
  7.     if(NULL == fp)  
  8.     {  
  9.     perror("error...");  
  10.         exit (1);  
  11.     }  
  12.     fclose (fp);  
  13.     fp = NULL;  
  14.      return 0;  
  15. }  
在檔案操作時,需要注意以下幾點問題
1、在定義檔案指標時,要將檔案指標指向空;如 FILE *fp = NULL;
2、需要判斷檔案是否開啟成功,如 if(NULL == fp)
3、檔案操作完成後,注意要將檔案關閉,否則會造成檔案所佔用記憶體洩露和在下次訪問檔案時出現問題。
4、檔案關閉後,需要將檔案指標指向空,這樣做會防止出現遊離指標,而對整個工程造成不必要的麻煩;如:fp = NULL;
  1. // 一個簡單的檔案壓縮程式
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <string.h>
  5. #define LEN 40
  6. int main (int argc, char *argv[])  
  7. {  
  8.     FILE *in, *out;  
  9.     int ch;  
  10.     char name[LEN];  
  11.     int count = 0;  
  12.     if (argc < 2)  
  13.     {  
  14.         fprintf (stderr, "Usage: %s filename\n", argv[0]);  
  15.         exit (1);  
  16.     }  
  17.     if ((in = fopen (argv[1], "r")) == NULL)  
  18.     {  
  19.         fprintf (stderr, "I couldn't open the file \"s\"\n", argv[1]);  
  20.         exit (2);  
  21.     }  
  22.     strcpy (name, argv[1]);  
  23.     strcat (name, ".red");  
  24.     if ((out = fopen (name, "w")) == NULL)  
  25.     {  
  26.         fprintf (stderr, "Can't Create output file.\n");  
  27.         exit (3);  
  28.     }  
  29.     while ((ch = getc (in)) != EOF)  
  30.         if (count++ % 3 == 0)  
  31.             putc (ch, out);  
  32.     if (fclose (in) != 0 || fclose (out) != 0)  
  33.         fprintf (stderr, "Error in closing files\n");  
  34.     return 0;  
  35. }  
  36. 同一目錄下建立檔案eddy,裡面新增內容 So even Eddy came oven ready .  
  37. 輸出結果:建立 eddy.red  
  38. Send money  

操作檔案 -- fread ( )函式和fwrite ( )函式

fwrite ( )函式

函式功能:
指向檔案寫入一個數據塊。
函式原型:
size_t fwrite(const void* buffer, size_t size, size_t count, FILE* stream);
注意:這個函式以二進位制形式對檔案進行操作,不侷限於文字檔案
引數:
(1)buffer:是一個指標,對fwrite來說,是要獲取資料的地址;
(2)size:要寫入內容的單位元組數;(size_t是sizeof返回的型別,通常是unsigned int型別)
(3)count:要進行寫入size位元組的資料項的個數;
(4)stream:目標檔案指標;
返回值:
返回實際寫入的資料塊數目 count。

fread ( )函式:

函式原型
size_t fread ( void *buffer, size_t size, size_t count, FILE *stream);
引數:
(1)buffer:用於接收資料的記憶體地址
(2)size:要讀的每個資料項的位元組數,單位是位元組 (size_t是sizeof返回的型別,通常是unsigned int型別)
(3)count:要讀count個數據項,每個資料項size個位元組.
(4)stream:輸入流
返回值:
返回真實寫入的項數,若大於count則意味著產生了錯誤。另外,產生錯誤後,檔案位置指示器是無法確定的。若其他stream或buffer為空指標,或在unicode模式中寫入的位元組數為奇數,此函式設定errno為EINVAL以及返回0.
函式功能:
從一個檔案流中讀資料,最多讀取count個項,每個項size個位元組,如果呼叫成功返回實際讀取到的項個數(小於或等於count),如果不成功或讀到檔案末尾返回 0。

  1. #include <stdio.h>
  2. #include <string.h>
  3. int main()  
  4. {  
  5.    FILE *fp;  
  6.    char c[] = "This is w3cschool";  
  7.    char buffer[20];  
  8.    /* 開啟檔案用於讀寫 */
  9.    fp = fopen("file.txt""w+");  
  10.    /* 寫入資料到檔案 */
  11.    fwrite(c, strlen(c) + 1, 1, fp);  
  12.    /* 查詢檔案的開頭 */
  13.    fseek(fp, SEEK_SET, 0);  
  14.    /* 讀取並顯示資料 */
  15.    fread(buffer, strlen(c)+1, 1, fp);  
  16.    printf("%s\n", buffer);  
  17.    fclose(fp);  
  18.    return(0);  
  19. }  

隨機存取:fseek ( )、ftell ( )、rewind ( )

fseek ( )函式

函式功能:

重定位流(資料流/檔案)上的檔案內部位置指標

注意:檔案指標指向檔案/流。位置指標指向檔案內部的位元組位置,隨著檔案的讀取會移動,檔案指標如果不重新賦值將不會改變或指向別的檔案。

函式原型:

int fseek(FILE *stream, long offset, int fromwhere);

引數:

(1)stream:為檔案指標
(2)offset:為偏移量,正數表示正向偏移,負數表示負向偏移(數字值用3L、10L等,L字尾表示long型別)
(3)origin:設定從檔案的哪裡開始偏移,可能取值為:SEEK_CUR、 SEEK_END 或 SEEK_SET
SEEK_SET: 檔案開頭
SEEK_CUR: 當前位置
SEEK_END: 檔案結尾
其中SEEK_SET,SEEK_CUR和SEEK_END依次為0,1和2.

返回值:

成功,返回 0,失敗返回 -1,並設定error的值,可以用perror()函式輸出錯誤。

函式描述:

函式設定檔案指標stream的位置。如果執行成功,stream將指向以fromwhere(偏移起始位置:檔案頭0(SEEK_SET),當前位置1(SEEK_CUR),檔案尾2(SEEK_END))為基準,偏移offset(指標偏移量)個位元組的位置。如果執行失敗(比如offset超過檔案自身大小),則不改變stream指向的位置。

上面這句話意思是,函式執行之後,檔案指標就移動到了fromwhere + offset位置處,如果offset超過檔案自身大小,則不改變stream指向的位置。

fseek函式和lseek函式類似,但lseek返回的是一個off_t數值,而fseek返回的是一個整型.

#include <stdio.h>
int main (void)
{
	char ch = 0;
	FILE *fp = fopen ("abc.txt", "r");
	if (fp)
	{
		//ABCDEFGHIGKLMN
		fseek (fp, 2L, SEEK_SET); //檔案開頭  (ABC)
		//(2+0 = 2 檔案指標移動到2的位置)
		fread (&ch,sizeof (char), 1, fp);
		printf ("%c\n", ch);
 
 		fseek (fp, 3L, SEEK_CUR); //當前位置 (CDEFG)
 		//(3+1 = 4 檔案指標移動到4的位置)
 		fread (&ch,sizeof (char), 1, fp); 
 		printf ("%c\n", ch);
 		
 		fseek (fp, -3L, SEEK_END); //檔案結尾 MN) 
 		//(-3+2 = -1 檔案指標移動到-1位置)
 		fread (&ch,sizeof (char), 1, fp); 
 		printf ("%c\n", ch);
 		
 		fclose (fp); 
 		fp = NULL;
	} 
	return 0;
}
輸出結果:
C
G
M

ftell ()函式

函式原型:

long ftell(FILE *stream);

函式功能:
函式 ftell() 用於得到檔案位置指標當前位置相對於檔案首的偏移位元組數。在隨機方式存取檔案時,由於檔案位置頻繁的前後移動,程式不容易確定檔案的當前位置。使用fseek函式後再呼叫函式ftell()就能非常容易地確定檔案的當前位置。

返回值:

以一個long型別值返回一個檔案的當前位置。如果發生錯誤,則返回 -1L,全域性變數 errno 被設定為一個正值。

呼叫示例編輯:
ftell(fp);利用函式 ftell() 也能方便地知道一個檔案的長。如以下語句序列: fseek(fp, 0L,SEEK_END); len =ftell(fp); 首先將檔案的當前位置移到檔案的末尾,然後呼叫函式ftell()獲得當前位置相對於檔案首的位移,該位移值等於檔案所含位元組數。

#include <stdio.h>
int main (void)
{
	FILE *fp;
	int len;
	//ABCDEF
	fp = fopen ("abc.txt", "r");
	if (fp == NULL)
	{
		perror ("error");
		return -1;
	}
	fseek (fp, 0, SEEK_END);
	len = ftell (fp);
	fclose (fp);
	printf ("abc.txt 的總大小 = %d 位元組\n", len);
	return 0;
}

輸出結果:
abc.txt 的總大小 = 8 位元組

rewind ()函式:

函式原型:
void rewind(FILE *stream)
返回值:
該函式不返回任何值。
函式功能: 
將檔案內部的位置指標重新指向一個流(資料流/檔案)的開頭
注意:不是檔案指標而是檔案內部的位置指標,隨著對檔案的讀寫檔案的位置指標(指向當前讀寫位元組)向後移動。而檔案指標是指向整個檔案,如果不重新賦值檔案指標不會改變。
rewind函式作用等同於 (void)fseek(stream, 0L, SEEK_SET);[1] 
#include <stdio.h>

int main()
{
   char str[] = "Hello World!";
   FILE *fp;
   int ch;

   /* 首先讓我們在檔案中寫入一些內容 */
   fp = fopen( "file.txt" , "w" );
   fwrite(str , 1 , sizeof(str) , fp );
   fclose(fp);

   fp = fopen( "file.txt" , "r" );
   while(1)
   {
      ch = fgetc(fp);
      if( feof(fp) )
      {
          break ;
      }
      printf("%c", ch);
   }
   
   rewind(fp); //從頭從新開始列印
  
   printf("\n");
   while(1)
   {
      ch = fgetc(fp);
      if( feof(fp) )
      {
          break ;
      }
      printf("%c", ch);
     
   }
   printf ("\n");
   fclose(fp);

   return(0);
}
輸出結果:
Hello World!
Hello World!