C語言文件操作
文章目錄
- C語言文件操作
- 1. 文件的概念
- 2. 二進制文件和文本文件
- 3. 文件的打開和關閉
- 3.1 流和標準流
- 3.1.1 流
- 3.1.2 標準流
- 3.2 文件指針
- 3.3 文件的打開和關閉
- 4. 文件的順序讀寫
- 4.1 順序讀寫函數
- 4.2 對比兩組函數
- 4.2.1 scanf/fscanf/sscanf
- 4.2.2 printf/fprintf/sprintf
- 5. 文件的隨機讀寫
- 5.1 fseek
- 5.2 ftell
- 6.3 rewind
- 6. 文件讀取結束的判定
- 6.1 被錯誤使用的feof
- 6.2 判斷讀取結束
- 7. 文件緩沖區
1. 文件的概念
-
文件的作用:
文件是用來存儲數據的,在之前寫的代碼中,只要我們對出程序,之前的數據就不復存在了,如果想要將數據保存到電腦上,就需要使用文件。
-
文件的分類:程序文件、數據文件(從文件功能的角度來分類)
-
程序文件:
程序文件包括源程序文件(.c),目標文件(Windows下為.obj),可執行程序(Windows下為.exe)
-
數據文件:
內容不一定是程序,而是程序運行時讀寫的數據,比如程序允許需要從中讀取數據的文件,或者輸出內容的文件。
-
文件標識:
一個文件一定要有一個唯一的文件標識,以便識別和使用,文件標識分為三個部分:文件路徑+文件名主干+文件后綴,例如:
C:\code\test.c
,為了方便起見,文件標識常被稱為文件名。
-
2. 二進制文件和文本文件
根據數據的組織形式,數據文件被稱為文本文件或二進制文件。
數據在內存中以二進制的形式存儲,如果不加轉換的輸出到外存,就是二進制文件。
如果要求外層加上ASCII碼的形式存儲,則需要在存儲前進行轉換。以ASCII字符的形式存儲的文件就是文本文件。
存儲的方式:字符一律以ASCII碼形式存儲,數值型數據既可以使用ASCII碼形式存儲,也可以使用二進制形式存儲。
如果有一個整數10000,如果以ASCII碼的形式輸出到磁盤,則磁盤中占用5個字節(每一個字符一個字節),而二進制形式輸出,則在磁盤上只占4個字節。
3. 文件的打開和關閉
3.1 流和標準流
3.1.1 流
我們程序的數據需要輸出到各種外部設備,也需要從外部設備獲取數據,不同大外部設備的輸入輸出操作各不相同,為了方便程序員對各種設備進行方便的錯做,我們抽象出了流的概念。可以把流理解為一條流淌著字符的河流。
C程序針對文件、畫面、鍵盤等的數據輸入輸出都是對流進行操作的。
一般情況下,我們想要向流里寫數據,或者從流中讀取數據,都是要打開流,然后操作。
3.1.2 標準流
我們發現,我們之前的程序中,向鍵盤輸入數據,向屏幕上輸出數據,并沒有打開流。
那是因為C語言程序在啟動的時候默認打開了3個流:
stdin
- 標準輸入流,大多數情況的環境中從鍵盤輸入。stdout
- 標準輸出流,大多數情況的環境中輸出至顯示屏。stderr
- 標準錯誤流,大多數情況的環境中輸出至顯示屏。
因為默認打開了這三個流,我們使用的scanf、printf等函數就可以直接進行輸入輸出操作。
stdin
、stdout
、stderr
這三個文件的類型是:FILE*
,通常稱為文件指針。
C語言中就是通過FILE*
的文件指針來維護流的各種操作的。
3.2 文件指針
緩沖文件系統中,關鍵的概念是文件類型指針,簡稱文件指針。
每個被使用的文件都在內存中開辟了一個相應的文件信息區,用來存放文件的相關信息(如文件的名字,文件狀態,及文件當前的位置等)。這些信息是保存在一個結構體變量中的。該結構體類型是由系統聲明的,取名為FILE。
下面是VS2013編譯環境中提供的stdio.h
頭文件中的文件類型聲明。
struct _iobuf {char *_ptr;int _cnt;char *_base;int _flag;int _file;int _charbuf;int _bufsiz;char *_tmpfname;
};
typedef struct _iobuf FILE;
不同的C編譯器的FILE類型包含的內容不完全相同,但是大同小異(VS2022環境下的FILE類型和上面的似乎就不太一樣)。
每當打開一個文件的時候,系統回根據文件的情況自動創建一個FILE結構的變量,并填充其中的信息,使用者不必關心細節。
一般都是通過一個FILE的指針來維護這個FILE結構的變量,這樣使用起來更加方便。
創建FILE*指針變量:
FILE* fp;
pf是一個指向FILE類型數據的指針變量。可以使某個pf指向某個文件的文件信息區(是一個結構體變量)。通過該文件信息區中的信息就能夠訪問該文件。也就是說,通過文件指針變量能夠間接找到與它關聯的文件。
3.3 文件的打開和關閉
文件在讀寫之前應該要先打開文件,在使用結束后應該關閉文件。
在編寫程序的時候,在打開文件的同時,都會返回應該FILE*的指針變量指向該文件,也相當于建立了指針和文件的關系。
ANSIC規定使用fopen
函數來打開文件,fclose
函數來關閉文件。
FILE * fopen ( const char * filename, const char * mode );int fclose ( FILE * stream );
- filename:需要打開的文件名。
- stream:需要關閉的文件的文件指針。
- mode:文件的打開模式,如下表。
文件使用方式 | 含義 | 如果指定文件不存在 |
---|---|---|
“r”(只讀) | 打開一個已經存在的文本文件,只進行讀取數據 | 出錯 |
“w”(只寫) | 打開一個文本文件,如果文件存在,清空文件中原來的內容,只進行寫入數據 | 建立新的文件 |
“a”(追加) | 打開一個文本文件,向文本文件末尾添加數據 | 建立新的文件 |
“rb”(只讀) | 打開一個二進制文件,只進行讀取數據 | 出錯 |
“wb”(只寫) | 打開一個二進制文件,如果文件存在,清空文件中原來的內容,只進行寫入數據 | 建立新的文件 |
“ab”(追加) | 打開一個文本文件,向文本文件末尾添加數據 | 建立新的文件 |
“r+”(讀寫) | 打開一個文本文件,進行讀寫操作 | 出錯 |
“w+”(讀寫) | 打開一個文本文件,如果文件存在,清空文件中原來的內容,進行讀寫操作 | 建立新的文件 |
“a+”(讀寫) | 打開一個文本文件,在文件末尾進行讀寫操作 | 建立新的文件 |
“rb+”(讀寫) | 打開一個二進制文件,進行讀寫操作 | 出錯 |
“wb+”(讀寫) | 打開一個二進制文件,如果文件存在,清空文件中原來的內容,進行讀寫操作 | 建立新的文件 |
“ab+”(讀寫) | 打開一個二進制文件,在文件末尾進行讀寫操作 | 建立新的文件 |
#include <stdio.h>int main()
{// 打開文件FILE* pf = fopen("test.txt", "w");// 關閉文件if (pf != NULL)fclose(pf);return 0;
}
4. 文件的順序讀寫
4.1 順序讀寫函數
函數名 | 功能 | 適用于 |
---|---|---|
fgetc | 字符輸入函數 | 所有輸入流 |
fputc | 字符輸出函數 | 所有輸出流 |
fgets | 文本行輸入函數 | 所有輸入流 |
fputs | 文本行輸出函數 | 所有輸出流 |
fscanf | 格式化輸入函數 | 所有輸入流 |
fprintf | 格式化輸出函數 | 所有輸出流 |
fread | 二進制輸入函數 | 文件 |
fwrite | 二進制輸出函數 | 文件 |
上面的適用于所有輸入流一般指適用于標準輸出流和其他輸出流(如文件輸入流),所有輸出流一般指的是標準輸出流和其他輸出流(如文件輸出流)
4.2 對比兩組函數
4.2.1 scanf/fscanf/sscanf
int scanf ( const char * format, ... );
int fscanf ( FILE * stream, const char * format, ... );
int sscanf ( const char * s, const char * format, ...);
- scanf:這個函數我們前面使用的很多,作用就是從標準輸入流獲取(一般是鍵盤)數據。
- fscanf:從我們指定的流中獲取數據,比如從某文件中獲取數據,第一個參數傳遞文件指針即可,其他參數與scanf基本一致。
- sscanf:從s字符串中獲取數據,第一個參數傳遞字符串指針,其他參數與scanf基本一致。
上面三個函數的返回值:讀取數據成功時,返回讀取成功的總項數,讀取數據失敗時,返回EOF。
4.2.2 printf/fprintf/sprintf
int printf ( const char * format, ... );
int fprintf ( FILE * stream, const char * format, ... );
int sprintf ( char * str, const char * format, ... );
- printf:這個函數在前面使用過很多次,作用就是將指定內容輸出到標準輸出流中(一般是屏幕)。
- fprintf:將數據輸出到指定的流中,比如輸出到某文件,第一個參數傳遞文件指針即可,其他參數與printf基本一致。
- sprintf:將數據輸出到指定字符串中(寫入到字符串中),第一個參數傳遞指定的字符串指針,其他參數與printf基本一致。
上面三個函數的返回值:輸出成功時,則返回輸出的總字符數,輸出失敗時,則返回一個負數。
5. 文件的隨機讀寫
5.1 fseek
重新設置文件位置指示器。
文件位置指示器類似于我們的鼠標光標,當打開文件時,文件位置指示器位于文件的開頭。
int fseek ( FILE * stream, long int offset, int origin );
- stream:文件指針
- offset:偏移量,決定將文件指示器移動至相對于origin位置多少偏移量的位置,正數在表向右移,負數代表向左移。
- origin:移動文件指針的相對位置。
- SEEK_SET:文件開頭位置。
- SEEK_CUR:文件指示器的當前位置。
- SEEK_END:文件結尾位置。
注:庫實現可以不真正支持 SEEK_END
(因此,使用它的代碼沒有真正的標準可移植性)。
#include <stdio.h>int main()
{FILE* pFile;pFile = fopen("example.txt", "wb");fputs("This is an apple.", pFile);fseek(pFile, 9, SEEK_SET);fputs(" sam", pFile);fclose(pFile);return 0;
}
5.2 ftell
long int ftell ( FILE * stream );
stream:文件指針。
返回文件指針相對于起始位置的偏移量。
#include <stdio.h>
int main()
{FILE* pFile;long size;pFile = fopen("myfile.txt", "rb");if (pFile == NULL)perror("Error opening file");else{fseek(pFile, 0, SEEK_END); // non-portablesize = ftell(pFile);fclose(pFile);printf("Size of myfile.txt: %ld bytes.\n", size);}return 0;
}
6.3 rewind
void rewind ( FILE * stream );
讓文件指針的位置回到文件的起始位置。
#include <stdio.h>
int main()
{int n;FILE* pFile;char buffer[27];pFile = fopen("myfile.txt", "w+");for (n = 'A'; n <= 'Z'; n++)fputc(n, pFile);rewind(pFile);fread(buffer, 1, 26, pFile);fclose(pFile);buffer[26] = '\0';printf(buffer);return 0;
}
6. 文件讀取結束的判定
6.1 被錯誤使用的feof
int feof ( FILE * stream );
檢查文件結束指示符。
當一個文件位置指示器讀取數據嘗試讀取到文件末尾或之后時,文件結束指示符會被設置。
當結束指示符被設置時,feof將返回非零值,否則返回0;
當未到文件末尾時,讀取就已經結束,這種情況文件結束指示符不會被設置,feof將返回0。
所以我們不能用feof判斷讀取是否結束。
6.2 判斷讀取結束
- 文本文件讀取是否結束可以判斷讀取函數的返回值是否為EOF(如fgetc)或NULL(如fgets)。
- 二進制文件的讀取結束判斷,判斷返回值是否小于實際要讀的個數,如使用fread判斷返回值是否小于實際要讀的個數。
7. 文件緩沖區
ANSIC標準采用緩沖文件系統處理數據文件,所謂文件緩沖系統就是指系統自動在內存中為程序中每個正在使用的文件開辟一塊文件緩沖區。從內存向磁盤輸出數據會先送到內存中的緩沖區,裝滿緩沖區后才一起送到磁盤上。如果從磁盤向計算機讀入數據,則從磁盤文件讀取數據輸入到內存緩沖區(充滿緩沖區),然后再沖緩沖區逐個地將數據送到程序數據區(程序變量等)。緩沖區的大小根據C編譯系統決定。