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

C語言文件讀寫操作完全指南:從基礎到進階實戰

     編輯:關於C語言

C語言文件讀寫操作完全指南

文件操作是C語言程式設計中的核心技能之一。無論是數據持久化、配置文件讀取,還是日誌記錄,都離不開文件的讀寫。本文將系統梳理C語言標準庫提供的文件操作函數,並通過完整的代碼示例演示各種讀寫模式的實際應用。

核心函數速查表

在深入代碼之前,先建立一個清晰的函數分類認知:

  • 字符級操作:fgetc / fputc - 單字符讀寫
  • 行級操作:fgets / fputs - 按行讀寫字符串
  • 塊級操作:fread / fwrite - 二進制數據塊讀寫,適合結構體數組
  • 格式化操作:fscanf / fprintf - 類似scanf/printf的文件版本

文件打開模式詳解

fopen的模式字符串組合決定了文件的訪問方式:

模式含義文件存在文件不存在
r只讀打開失敗返回NULL
w只寫截斷長度為0創建新文件
a追加定位到文件末尾創建新文件
r+讀寫打開失敗
w+讀寫截斷創建
a+讀寫追加打開,定位末尾創建

加上b表示二進制模式(如rb、wb+),在Windows下處理換行符轉換時尤為重要。

完整實戰代碼解析

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

/* 
 * 關鍵常量與函數原型說明:
 * EOF = -1,表示文件結束或錯誤
 * fgets最多讀取 size-1 個字符,遇換行或EOF停止
 * fputs寫入時不包含字符串結束符'\0'
 * fread/fwrite返回實際讀寫的項數,若不等於預期count則可能出錯或到達EOF
 */

FILE *fp = NULL;

/* === 讀取函數族 === */

/* 字符讀取:逐個讀取,適合簡單文本處理 */
void GetCharFromFile(FILE *fp) {
    int ch;
    while ((ch = fgetc(fp)) != EOF) {
        printf("fgetc: [%c]\n", ch);
    }
}

/* 行讀取:緩衝區讀取,保留換行符,高效處理文本行 */
void GetLineFromFile(FILE *fp) {
    char line[1024];
    while (fgets(line, sizeof(line), fp) != NULL) {
        printf("fgets: [%s]", line);  // line已包含換行符
    }
}

/* 塊讀取:二進制讀取,適合結構體數組 */
void GetBlockFromFile(FILE *fp) {
    int block[10];
    int count = 1;
    while (fread(block, sizeof(block), count, fp) == count) {
        printf("fread: ");
        for (int i = 0; i < 10; i++) printf("%d ", block[i]);
        printf("\n");
    }
}

/* 格式化讀取:按指定格式解析,返回成功讀取的項數 */
void ReadFormatToFile(FILE *fp) {
    char file[32], func[32], date[32];
    int line;
    if (fscanf(fp, "%s %s %d %[^\n]", file, func, &line, date) == 4) {
        printf("file: %s\nfunc: %s\nline: %d\ndate: %s\n", file, func, line, date);
    }
}

/* === 寫入函數族 === */

int WriteCharToFile(FILE *fp, char ch) {
    return fputc(ch, fp) != EOF ? 0 : -1;
}

int WriteStrToFile(FILE *fp, const char *str) {
    return fputs(str, fp) != EOF ? 0 : -1;
}

int WriteBlockToFile(FILE *fp, const void *block, int size, int count) {
    return fwrite(block, size, count, fp) == count ? 0 : -1;
}

int WriteFormatToFile(FILE *fp) {
    return fprintf(fp, "%s %s %d %s\n", __FILE__, __func__, __LINE__, __DATE__) >= 0 ? 0 : -1;
}

/* === 主程序演示 === */
int main(void) {
    char ch = 'R';
    char *str = "Hello World.\n";
    int block[10] = {1,2,3,4,5,6,7,8,9,0};
    const char *filePath = "./test_io.dat";

    /* w+ 模式:文件不存在則創建,存在則清空,可讀可寫 */
    fp = fopen(filePath, "w+");
    if (!fp) {
        perror("fopen failed");
        return -1;
    }

    /* 執行寫入操作 */
    printf("WriteStrToFile: %s\n", WriteStrToFile(fp, str) ? "Fail" : "Success");
    // WriteCharToFile(fp, ch);
    // WriteBlockToFile(fp, block, sizeof(block), 1);
    // WriteFormatToFile(fp);

    /* 關鍵步驟:rewind將文件指針重置到開頭,以便後續讀取 */
    rewind(fp);

    /* 執行讀取操作驗證寫入結果 */
    GetLineFromFile(fp);
    // GetCharFromFile(fp);
    // GetBlockFromFile(fp);
    // ReadFormatToFile(fp);

    fclose(fp);
    return 0;
}

關鍵技術點深度解析

1. 緩衝區機制與刷新

C標準庫文件操作默認是全緩衝(塊設備)或行緩衝(終端)。fflush(fp)可強制刷新輸出緩衝區;setbuf/setvbuf可自定義緩衝策略。調試時若發現數據未寫入文件,首先檢查是否漏掉了fclose或fflush。

2. 文件指針定位控制

  • rewind(fp):等價於fseek(fp, 0, SEEK_SET),並清除錯誤標記
  • fseek(fp, offset, whence):精確定位,whence可為SEEK_SET(開頭)、SEEK_CUR(當前)、SEEK_END(結尾)
  • ftell(fp):獲取當前位置偏移量

3. 錯誤處理最佳實踐

if ((fp = fopen("data.bin", "rb")) == NULL) {
    perror("fopen");  // 輸出系統錯誤信息
    return -1;
}

/* 操作後檢查錯誤標記 */
if (ferror(fp)) {
    fprintf(stderr, "文件操作發生錯誤\n");
}
clearerr(fp);  // 清除錯誤和EOF標記,繼續操作

4. 二進制 vs 文本模式的陷阱

Windows下文本模式會將\n轉換為\r\n,導致fread/fwrite處理結構體時數據長度不匹配。處理二進制數據(結構體、圖片、序列化數據)務必使用"b"模式。

常見錯誤避坑指南

  1. feof誤用:while(!feof(fp))模式會多讀一次,正確寫法是直接檢查讀取函數返回值
  2. 緩衝區溢出:fgets需指定正確大小,fscanf的%s需限制寬度如%31s
  3. 結構體對齊導致的跨平台不兼容:二進制寫入結構體時,不同編譯器/架構的padding不同,跨平台傳輸建議使用序列化庫或手動打包
  4. 忘記檢查fopen返回值:永遠假設文件打開可能失敗

進階應用場景

  • 配置文件解析:結合fgets+sscanf/strtok實現INI格式解析
  • 大文件分塊處理:使用fseek實現隨機訪問,配合mmap(POSIX)提升性能
  • 臨時文件:tmpfile()創建自動刪除的臨時文件,mkstemp生成唯一臨時文件名
  • 文件鎖與併發:flock/fcntl實現進程間文件訪問協調

掌握文件操作的關鍵在於理解流的概念、緩衝機制以及錯誤狀態的傳遞。建議在實際項目中封裝一層錯誤處理統一、支持RAII風格資源管理的文件操作工具類,大幅降低資源洩漏和邏輯錯誤風險。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved