「如何把文件中的數據賦到結構體成員中?」是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 庫 | 標準化,工具鏈豐富 | 引入依賴 |
| 批量文件處理 | 目錄遍歷 + 動態數組 + 篩選回調 | 內存可控,邏輯解耦 | 需處理平台差異 |
掌握「文本解析靈活、二進制高效、批量處理模組化」三種模式,並建立「輸入驗證、錯誤處理、資源管理」工程化習慣,即可從容應對C語言項目中各類文件數據讀取需求。