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”並輸出出錯原因。
- 示例一:
- #include<stdio.h>
- int main(void)
- {
- FILE*fp = NULL;
- fp = fopen("abc.txt", "r");
- if(NULL == fp)
- {
- perror("error...");
- exit (1);
- }
- fclose (fp);
- fp = NULL;
- return 0;
- }
1、在定義檔案指標時,要將檔案指標指向空;如 FILE *fp = NULL;
2、需要判斷檔案是否開啟成功,如 if(NULL == fp)
3、檔案操作完成後,注意要將檔案關閉,否則會造成檔案所佔用記憶體洩露和在下次訪問檔案時出現問題。
4、檔案關閉後,需要將檔案指標指向空,這樣做會防止出現遊離指標,而對整個工程造成不必要的麻煩;如:fp = NULL;
- // 一個簡單的檔案壓縮程式
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #define LEN 40
- int main (int argc, char *argv[])
- {
- FILE *in, *out;
- int ch;
- char name[LEN];
- int count = 0;
- if (argc < 2)
- {
- fprintf (stderr, "Usage: %s filename\n", argv[0]);
- exit (1);
- }
- if ((in = fopen (argv[1], "r")) == NULL)
- {
- fprintf (stderr, "I couldn't open the file \"s\"\n", argv[1]);
- exit (2);
- }
- strcpy (name, argv[1]);
- strcat (name, ".red");
- if ((out = fopen (name, "w")) == NULL)
- {
- fprintf (stderr, "Can't Create output file.\n");
- exit (3);
- }
- while ((ch = getc (in)) != EOF)
- if (count++ % 3 == 0)
- putc (ch, out);
- if (fclose (in) != 0 || fclose (out) != 0)
- fprintf (stderr, "Error in closing files\n");
- return 0;
- }
- 同一目錄下建立檔案eddy,裡面新增內容 So even Eddy came oven ready .
- 輸出結果:建立 eddy.red
- 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。
- #include <stdio.h>
- #include <string.h>
- int main()
- {
- FILE *fp;
- char c[] = "This is w3cschool";
- char buffer[20];
- /* 開啟檔案用於讀寫 */
- fp = fopen("file.txt", "w+");
- /* 寫入資料到檔案 */
- fwrite(c, strlen(c) + 1, 1, fp);
- /* 查詢檔案的開頭 */
- fseek(fp, SEEK_SET, 0);
- /* 讀取並顯示資料 */
- fread(buffer, strlen(c)+1, 1, fp);
- printf("%s\n", buffer);
- fclose(fp);
- return(0);
- }
隨機存取: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!