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

C語言文件讀取結構體數據完整指南:從單文件到批量處理

     編輯:C 語言問答

C語言文件讀取結構體數據完整指南:從單文件到批量處理

「如何把文件中的數據賦到結構體成員中?」是C語言文件操作的高頻需求。本文從文本/二進制讀取、格式化解析、錯誤處理、批量文件處理、內存管理五個維度,提供生產級代碼模式與最佳實踐。

一、基礎場景:固定格式文本文件讀取

文件resume.txt內容:

姓名:犀利哥
性別:男
年齡:十二歲
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX_LEN 100

typedef struct {
    char name[MAX_LEN];
    char sex[MAX_LEN];
    char age[MAX_LEN];
} Student;

// 安全讀取一行,去除換行符
char* safe_fgets(char *buf, int size, FILE *fp) {
    if (!fgets(buf, size, fp)) return NULL;
    buf[strcspn(buf, "\n")] = '\0'; // 移除\n
    return buf;
}

int main() {
    Student stu = {0};
    FILE *fp = fopen("D:\\resume.txt", "r");
    if (!fp) {
        perror("文件打開失敗");
        return 1;
    }
    
    char line[MAX_LEN];
    // 方法1: fscanf 格式化讀取 (要求格式極其固定)
    if (fscanf(fp, "姓名:%99[^\n]\n", stu.name) != 1 ||
        fscanf(fp, "性別:%99[^\n]\n", stu.sex) != 1 ||
        fscanf(fp, "年齡:%99[^\n]\n", stu.age) != 1) {
        fprintf(stderr, "格式解析失敗\n");
        fclose(fp);
        return 1;
    }
    fclose(fp);
    
    printf("姓名:%s\n性別:%s\n年齡:%s\n", stu.name, stu.sex, stu.age);
    return 0;
}

二、進階:健壯的鍵值對解析 (適應字段順序變化)


int parse_kv_file(const char *filename, Student *stu) {
    FILE *fp = fopen(filename, "r");
    if (!fp) return -1;
    
    char line[MAX_LEN];
    while (safe_fgets(line, sizeof(line), fp)) {
        char *colon = strchr(line, ':'); // 中文冒號
        if (!colon) colon = strchr(line, ':'); // 英文冒號兼容
        if (!colon) continue; // 忽略無效行
        
        *colon = '\0';
        char *key = line;
        char *value = colon + 1;
        
        if (strcmp(key, "姓名") == 0) strncpy(stu->name, value, MAX_LEN-1);
        else if (strcmp(key, "性別") == 0) strncpy(stu->sex, value, MAX_LEN-1);
        else if (strcmp(key, "年齡") == 0) strncpy(stu->age, value, MAX_LEN-1);
    }
    fclose(fp);
    return 0;
}

三、二進制文件讀寫 (高性能、結構體直接映射)


// 寫入
int write_bin(const char *filename, const Student *stu) {
    FILE *fp = fopen(filename, "wb");
    if (!fp) return -1;
    size_t written = fwrite(stu, sizeof(Student), 1, fp);
    fclose(fp);
    return written == 1 ? 0 : -1;
}

// 讀取
int read_bin(const char *filename, Student *stu) {
    FILE *fp = fopen(filename, "rb");
    if (!fp) return -1;
    size_t read = fread(stu, sizeof(Student), 1, fp);
    fclose(fp);
    return read == 1 ? 0 : -1;
}

// 注意:結構體含指針/柔性數組時不可直接二進制序列化
// 跨平台需處理對齊、字節序、指針寬度差異

四、批量處理多份簡歷:動態數組+目錄遍歷


#include <dirent.h> // Linux/POSIX
// Windows需用 FindFirstFile/FindNextFile

#define MAX_STUDENTS 1000

typedef struct {
    Student *data;
    size_t count;
    size_t capacity;
} StudentArray;

int init_array(StudentArray *arr, size_t init_cap) {
    arr->data = malloc(init_cap * sizeof(Student));
    if (!arr->data) return -1;
    arr->count = 0;
    arr->capacity = init_cap;
    return 0;
}

int push_student(StudentArray *arr, const Student *stu) {
    if (arr->count >= arr->capacity) {
        size_t new_cap = arr->capacity * 2;
        Student *new_data = realloc(arr->data, new_cap * sizeof(Student));
        if (!new_data) return -1;
        arr->data = new_data;
        arr->capacity = new_cap;
    }
    arr->data[arr->count++] = *stu; // 結構體賦值拷貝
    return 0;
}

void free_array(StudentArray *arr) {
    free(arr->data);
    arr->data = NULL;
    arr->count = arr->capacity = 0;
}

// 篩選邏輯示例
int is_candidate_ok(const Student *stu) {
    // 例如:年齡數值在20-35之間,性別為男
    int age = atoi(stu->age); // 簡化:假設年齡存純數字
    return (age >= 20 && age <= 35 && strcmp(stu->sex, "男") == 0);
}

int process_directory(const char *dir_path, StudentArray *result) {
    DIR *dir = opendir(dir_path);
    if (!dir) return -1;
    
    struct dirent *entry;
    char filepath[512];
    Student stu;
    
    while ((entry = readdir(dir)) != NULL) {
        if (entry->d_type != DT_REG) continue; // 只處理普通文件
        if (strstr(entry->d_name, ".txt") == NULL) continue; // 只處理.txt
        
        snprintf(filepath, sizeof(filepath), "%s/%s", dir_path, entry->d_name);
        memset(&stu, 0, sizeof(stu));
        
        if (parse_kv_file(filepath, &stu) == 0) {
            if (is_candidate_ok(&stu)) {
                push_student(result, &stu);
                printf("錄取: %s (%s, %s)\n", stu.name, stu.sex, stu.age);
            } else {
                printf("不符合: %s\n", stu.name);
            }
        }
    }
    closedir(dir);
    return 0;
}

int main() {
    StudentArray candidates;
    init_array(&candidates, 100);
    
    process_directory("D:\\resumes", &candidates);
    
    printf("\n=== 錄取名單 (%zu人) ===\n", candidates.count);
    for (size_t i = 0; i < candidates.count; i++) {
        printf("%zu. %s %s %s\n", i+1, 
               candidates.data[i].name,
               candidates.data[i].sex,
               candidates.data[i].age);
    }
    
    free_array(&candidates);
    return 0;
}

五、關鍵技術點總結

場景推薦方法優點缺點
固定格式文本fscanf + 格式字符串代碼簡潔極其脆弱,格式稍變即失敗
鍵值對/類INI文本逐行讀取 + strchr/strtok 解析健壯,支持字段順序任意、註釋、擴展代碼較多
高性能/大數據量二進制 fread/fwrite 結構體極快,代碼最少不可跨平台/版本,含指針不可用
結構化配置JSON (cJSON) / TOML / YAML 庫標準化,工具鏈豐富引入依賴
批量文件處理目錄遍歷 + 動態數組 + 篩選回調內存可控,邏輯解耦需處理平台差異

六、避坑檢查單

  • ✅ 所有文件操作檢查返回值 (`fp == NULL`, `fscanf != 項目數`)
  • ✅ 緩衝區大小限制 (`%99s` 防止溢出)
  • ✅ 字符串複製用 `strncpy`/`snprintf` 確保以 `\0` 結尾
  • ✅ 動態分配檢查 `malloc/realloc` 返回值
  • ✅ 跨平台路徑分隔符處理 (`/` vs `\\`)
  • ✅ 編碼一致性 (源文件UTF-8,文件編碼匹配,寬字符API)
  • ✅ 大文件考慮流式處理而非全量讀入內存

掌握「文本解析靈活、二進制高效、批量處理模組化」三種模式,並建立「輸入驗證、錯誤處理、資源管理」工程化習慣,即可從容應對C語言項目中各類文件數據讀取需求。

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