首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C語言文件 I/O 完全指南:文本流與二進制流深度解析

     編輯:關於C語言

文件流的兩種模式

C 標準庫提供兩種文件流模式,本質上都是字節序列,區別在於數據的解釋方式:

  • 文本流:數據以字符形式組織,按行存儲(以 \n 分隔),整數/浮點數需轉換為字符串寫入。跨平台時可能進行換行符轉換(如 Windows \r\n ↔ Unix \n)。
  • 二進制流:直接讀寫內存中的字節表示,double 佔 8 字節,int 佔 4 字節,無任何轉換,精確保持內存位模式。
關鍵洞察:文件中都是字節序列,關鍵在於程序如何解釋它們。字符 'a' 存儲為 ASCII 97,可被解讀為字符或整數 97。

一、文本流核心函數

1. 文件打開與關閉

FILE *fopen(const char *path, const char *mode);
int fclose(FILE *fp);

常用模式:

模式說明
r只讀,文件必須存在
r+讀寫,文件必須存在
w只寫,截斷或創建
w+讀寫,截斷或創建
a追加寫入,創建或定位到末尾
a+追加讀寫

注意:讀寫切換時需調用 fflush() 刷新緩衝區,或用 fseek()/rewind() 重定位指針,否則可能導致數據丟失或覆蓋。

2. 字符級 I/O

int fputc(int c, FILE *stream);  // 寫入字符
int fgetc(FILE *stream);         // 讀取字符
// 對應宏版本:putc(), getc()

實際操作均通過內存緩衝區,非逐字符訪問磁盤。

3. 行級 I/O

int fputs(const char *s, FILE *stream);           // 寫入字符串(不含 \0)
char *fgets(char *s, int size, FILE *stream);     // 讀取一行(含 \n,以 \0 結尾)

4. 格式化 I/O

int fprintf(FILE *stream, const char *format, ...);
int fscanf(FILE *stream, const char *format, ...);

5. 緩衝區控制

int fflush(FILE *stream);  // 強制寫入磁盤
void rewind(FILE *stream); // 等同 fseek(fp, 0, SEEK_SET)

6. 錯誤處理最佳實踐

FILE *fp = fopen("test.txt", "r");
if (!fp) {
    fprintf(stderr, "無法打開文件: %s\n", strerror(errno));
    exit(EXIT_FAILURE);
}

二、二進制流操作

在模式字符串中添加 b:rb、wb、rb+ 等。

1. 塊讀寫

size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
  • size:單個元素字節數
  • nmemb:元素個數
  • 總字節數 = size * nmemb

2. 隨機訪問

long ftell(FILE *stream);                    // 獲取當前位置
int fseek(FILE *stream, long offset, int whence);  // 移動位置
// whence: SEEK_SET(開頭) SEEK_CUR(當前) SEEK_END(結尾)

int fgetpos(FILE *stream, fpos_t *pos);      // 獲取位置(可移植)
int fsetpos(FILE *stream, const fpos_t *pos); // 設置位置
注意:追加模式(a/a+)下不可修改寫入位置,數據總寫入文件末尾。

三、實戰代碼片段

文本文件逐行處理

char line[256];
while (fgets(line, sizeof(line), fp)) {
    // 處理每一行,line 包含換行符
    line[strcspn(line, "\n")] = '\0';  // 移除換行符
}

二進制結構體讀寫

typedef struct {
    int id;
    double score;
    char name[32];
} Student;

Student stu = {1001, 95.5, "張三"};
fwrite(&stu, sizeof(Student), 1, fp);  // 寫入
fread(&stu, sizeof(Student), 1, fp);   // 讀取

四、常見陷阱與建議

  • ❌ feof() 控制循環:while(!feof(fp)) 會多讀一次,應用讀取函數返回值判斷
  • ❌ 文本模式下使用 fread/fwrite:導致數據難以解讀
  • ✅ 總是檢查 fopen 返回值
  • ✅ 使用 stderr 輸出錯誤信息,避免被重定向
  • ✅ 關鍵數據寫入後調用 fflush() 或 fsync()(POSIX)確保落盤
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved