一切皆文件
---Linux
- 頭文件 <stdio.h> 中定義了文件的相關操作
#include <stdio.h>
| 文件操作基本流程: 打開:fopen 相關操作 關閉:fclose fopen函數原型:FILE *fopen(const char *filename, const char *mode) fopen函數打開filename指定的文件, 並返回一個與之相關聯的流。 如果打開操作失敗,則返回 NULL 訪問模式mode可以為下列合法值之一: "r" 打開文本文件用於讀 "w" 創建文本文件用於寫, 並刪除已經存在的內容(如果有的話) "a" 追加, 打開或創建文本文件,並向文件末尾追加內容 "r+" 打開文本文件用於更新(即讀和寫) "w+" 創建文本文件用於更新, 並刪除已經存在的內容(如果有的話) "a+" 追加, 打開或創建文本文件用於更新, 寫文件時追加到文件末尾 ps:如果在上述訪問模式之后加上 b , 如 "rb" 或 "w+b" 等,則表示對二進制文件進行操作 fclose函數原型:int fclose(FILE *fp) fclose函數的功能是關閉 fp 指向的文件 正常關閉返回0, 出錯時返回非0 ps:文件操作結束不關閉文件可能丟失數據 |
- 什么是文本文件?
在文本文件中數據是以字符形式呈現的,每個字符占用一個字節,而字節在計算機中又是以ASCII碼來識別的
在存儲文本文件時需要先將ASCII碼轉換為二進制的形式,然后進行存儲
比如存儲12的時候會按照字符 '1' 和 '2' 來存儲
'1' 的ASCII碼為49 轉化為二進制為 00110001 '2' 的ASCII碼為50 轉化為二進制為 00110010
存儲形式為 [00110001][00110010]
- 什么是二進制文件?
二進制文件在存儲數據時是直接以二進制的方式進行的, 存儲方式與數據在內存中的存儲方式相同
不需要進行轉換
優點:不僅可以提高執行效率(比如從磁盤直接讀取到內存不需要數據轉換),還可以節約存儲空間
比如存儲char類型的數字12則會將其二進制數 00001100直接進行存儲
存儲i形式為 [00001100]
- 文本文件和二進制文件的區別(Windows)
文本文件:寫入的時候會將換行符 '\n'(ASCII: 10) 解析為回車符 '\r'(ASCII:13)'\n'(ASCII:10)
讀取的時候又會將回車符 '\r'(ASCII:13)'\n'(ASCII:10)解析成換行符 '\n'(ASCII: 10)
二進制文件:原樣寫入讀出
- Linux系統下沒有區別
- 三個特殊的文件
stdin :標准輸入文件指針,系統分配為鍵盤
stdout :標准輸出文件指針,系統分配為顯示器
stderr :標准錯誤輸出文件指針,系統分配為顯示器
在文件操作時,系統自動與3個標准設備文件聯系,這3個文件無需打開和關閉
從文件中輸入和向文件輸出有兩個對應函數:
fprintf
函數原型為
int fprintf (FILE *, const char *, ...);
fscanf
函數原型為
int fscanf (FILE *, const char *, ...);
例如 printf("hello world!\n"); 等價於 fprintf(stdout, "hello world\n");
scanf("%d", &num); 等價於 fscanf(stdin, "%d", &num);
1 #include <stdio.h> 2 #include <stdlib.h> 3 4 int main() 5 { 6 int num; 7 fscanf(stdin, "%d", &num); 8 fprintf(stdout, "%d\n", num); 9 return 0; 10 }
- 文件型結構體
C語言提供“文件型”結構來標識記錄待操作文件的信息,該結構體定義在頭文件 stdio.h 中
形式為:
1 typedef struct _iobuf 2 { 3 char *_ptr; /* 當前緩沖區內容指針 */ 4 int _cnt; /* 緩沖區還有多少個字符 */ 5 char *_base; /* 緩沖區的起始地址 */ 6 int _flag; /* 文件流的狀態,是否錯誤或者結束 */ 7 int _file; /* 文件描述符 */ 8 int _charbuf; /* 雙字節緩沖,緩沖2個字節 */ 9 int _bufsiz; /* 緩沖區大小 */ 10 char *_tmpfname; /* 臨時文件名 */ 11 } FILE;
- 文件基本操作
打開與關閉文件 fopen fclose
1 #include <stdio.h> 2 #include <stdlib.h> 3 #define FILE_NAME "./1.txt" /* 存在的文件 */ 4 #define FILE_NAME_NO "./2.txt" /* 不存在的文件 */ 5 int main() 6 { 7 FILE *fp = fopen(FILE_NAME, "r"); 8 if(!fp){ 9 perror("|open file failed"); 10 return 1; 11 } 12 printf("|open the file %s successfully\n", FILE_NAME); 13 int i = fclose(fp); /*關閉成功返回 0*/ 14 printf("|%d\n", i); 15 16 FILE *fp_no = fopen(FILE_NAME_NO, "r"); 17 if(!fp_no){ 18 perror("|open file failed"); 19 return 1; 20 } 21 printf("|open the file %s successfully\n", FILE_NAME_NO); 22 int j = fclose(fp_no); 23 printf("|%d\n", j); 24 return 0; 25 }
輸出
|open the file ./1.txt successfully |0 |open file failed: No such file or directory
字符讀寫函數 fgetc fputc
函數原型
1 int fgetc (FILE *); 2 3 int fputc (int, FILE *);

1 #include <stdio.h> 2 #include <stdlib.h> 3 #define FILE_NAME "./1.txt" /* 存在的文件 */ 4 int main() 5 { 6 FILE *fp = fopen(FILE_NAME, "r"); 7 if(!fp){ 8 perror("|open file failed"); 9 return 1; 10 } 11 printf("|open the file %s successfully\n", FILE_NAME); 12 char c = fgetc(fp); 13 printf("|%c\n", c); 14 fclose(fp); 15 return 0; 16 }
輸出
|open the file ./1.txt successfully
|h
1 #include <stdio.h> 2 #include <stdlib.h> 3 #define FILE_NAME "./2.txt" /* 不存在的文件 */ 4 int main() 5 { 6 FILE *fp = fopen(FILE_NAME, "w"); 7 if(!fp){ 8 perror("|open file failed"); 9 return 1; 10 } 11 printf("|open the file %s successfully\n", FILE_NAME); 12 fputc('h', fp); 13 fclose(fp); 14 return 0; 15 }
新建了 2.txt文件 並寫入一個字符 h

后續的函數相關操作類似,列出了函數原型,不舉代碼示例啦(偷懶ing)
字符串讀寫函數 fgets fputs
函數原型
1 int fputs (const char *, FILE *); 2 3 char * fgets (char *, int, FILE *);
格式化讀寫函數 fprintf fscanf
函數原型
1 int fprintf (FILE *, const char *, ...); 2 3 int fscanf (FILE *, const char *, ...);
數據塊讀寫函數 fread fwrite
函數原型
1 size_t fread (void *, size_t, size_t, FILE *); 2 3 size_t fwrite (const void *, size_t, size_t, FILE *);
這兩個函數用於讀寫數據塊,成功返回 塊數, 出錯或到文件尾返回 0
第一個參數: 指向要輸入/輸出數據塊的首地址的指針
第二個參數: 每個要讀/寫的數據塊的大小(字節數)
第三個參數: 要讀/寫的數據塊的個數
第四個參數: 要讀/寫的文件指針
fread 和 fwrite 函數一般用於 數組 結構體等塊的讀寫(多為二進制形式)
比如
1 #include <stdio.h> 2 #include <stdlib.h> 3 #define FILE_NAME "./3.txt" /* 不存在的文件 */ 4 5 int main(){ 6 7 int num[50] = {0}; 8 for(int i = 0; i < 50; ++i){ 9 num[i] = i; 10 } 11 FILE *fp = fopen(FILE_NAME, "w+b"); 12 fwrite(num, sizeof(int), 50, fp); 13 printf("指針到達:%d\n", (int)ftell(fp)); 14 rewind(fp); 15 printf("指針到達:%d\n", (int)ftell(fp)); 16 int newnum[50] = {0}; 17 fread(newnum, sizeof(int), 50, fp); 18 for(int i = 0; i < 50; ++i){ 19 printf(" %d", newnum[i]); 20 } 21 printf("\n"); 22 fclose(fp); 23 return 0; 24 }
得到 3.txt文件 內容:

可以看到一個int類型占有4個字節,並且存儲方式是低位在前高位在后
輸出結果:

判斷文件是否結束 feof
函數原型
int feof (FILE *);
feof函數用來判斷文件是否結束, 文件結束返回一個 非0值, 未結束則返回 0
文件出錯檢驗 ferror
函數原型
int ferror (FILE *);
ferror函數用來測試文件是否出錯, 未出錯返回 0, 出錯返回一個 非0值
文件定位 rewind fseek ftell
函數原型
1 int fseek (FILE *, long, int); /*根據參考位置靈活移動指針到目的地*/ 2 3 long ftell (FILE *); /*用來告訴我指針距離文件頭的字符數*/ 4 5 void rewind (FILE *); /*將指針重新移動到文件頭*/
其中 fseek函數第一個參數操作的文件指針(fp), 第二個參數是偏移量(offset) 第三個參數是參考點:0---文件開始 2---文件末尾 1---當前位置
刷新緩沖區 fflush
函數原型
int fflush (FILE *);
對於輸出流來說, fflush函數將已經寫到緩沖區但尚未寫入文件的所有數據寫到文件中
對輸入流來說, 其結果是未定義的
如果在寫的過程中發生錯誤,則返回EOF, 否則返回 0, fflush(NULL)將清洗所有的輸出流
文件刪除 remove
函數原型
int remove (const char *);
remove函數刪除指定的文件, 操作成功返回 0, 操作失敗返回一個 非0值
文件重命名 rename
函數原型
int rename (const char *, const char *);
修改文件名成功返回 0 , 操作失敗返回 非0值
創建臨時文件 tmpfile
函數原型
FILE * tmpfile (void);
tmpfile函數以 "wb+" 模式創建一個臨時文件(二進制的形式-->臨時文件當然讀寫更快),該文件在被關閉或程序正常結束時將被自動刪除
創建操作成功 函數返回一個流(文件的指針), 創建文件失敗返回NULL
創建一個臨時文件名 tmpnam
函數原型
char * tmpnam (char *);
tmpnam函數創建一個與現有文件名不同的字符串,並返回一個指向一內部靜態數組的指針,tmpnam(s)函數把創建的字符串保存到數組s中,並將它作為函數返回值返回
要注意的是數組s要具有足夠的空間來存儲這個文件名字符串
示例
1 #include <stdio.h> 2 3 int main() 4 { 5 char s[64] = {0}; 6 tmpnam(s); 7 printf("%s\n", s); 8 return 0; 9 }
輸出
\s804.
這是一個隨機的文件名(只要不與現有的沖突就OK)
后記:
-- 在進行文件操作的時候根據情況選擇合適的讀寫方式以及讀寫格式
-- 要靈活使用 feof fflush (!!!!切記緩沖問題) ftell fseek rewind 等函數
-- 文件使用結束切記 fclose 關閉文件, 避免出錯
