外观
07.文件文本处理函数详解
约 1581 字大约 5 分钟
文件文本处理函数C个人随笔
2025-12-12
一、文件打开与关闭
1. fopen() - 打开文件
FILE *fopen(const char *filename, const char *mode);模式说明:
"r"- 只读(文件必须存在)"w"- 只写(创建新文件或清空现有文件)"a"- 追加(在文件末尾添加)"r+"- 读写(文件必须存在)"w+"- 读写(创建新文件)"a+"- 读写追加
示例片段:
FILE *fp = fopen("data.txt", "r");
if (fp == NULL) {
perror("文件打开失败");
return -1;
}2. fclose() - 关闭文件
int fclose(FILE *stream);示例片段:
if (fclose(fp) != 0) {
perror("文件关闭失败");
}二、文本读取函数
1. fgetc() - 读取单个字符
int fgetc(FILE *stream);示例片段:
int ch;
while ((ch = fgetc(fp)) != EOF) {
putchar(ch);
}2. fgets() - 读取一行文本
char *fgets(char *str, int n, FILE *stream);示例片段:
char buffer[256];
while (fgets(buffer, sizeof(buffer), fp) != NULL) {
printf("读取的行: %s", buffer);
}3. fscanf() - 格式化读取
int fscanf(FILE *stream, const char *format, ...);示例片段:
char name[50];
int age;
float score;
while (fscanf(fp, "%49s %d %f", name, &age, &score) == 3) {
printf("姓名: %s, 年龄: %d, 分数: %.2f\n", name, age, score);
}三、文本写入函数
1. fputc() - 写入单个字符
int fputc(int ch, FILE *stream);示例片段:
char text[] = "Hello";
for (int i = 0; text[i] != '\0'; i++) {
fputc(text[i], fp);
}2. fputs() - 写入字符串
int fputs(const char *str, FILE *stream);示例片段:
char *lines[] = {"第一行\n", "第二行\n", "第三行\n"};
for (int i = 0; i < 3; i++) {
fputs(lines[i], fp);
}3. fprintf() - 格式化写入
int fprintf(FILE *stream, const char *format, ...);示例片段:
fprintf(fp, "姓名: %s\n", "张三");
fprintf(fp, "年龄: %d\n", 25);
fprintf(fp, "工资: %.2f\n", 8500.50);四、二进制文件读写函数
1. fread() – 从文件读取数据块
size_t fread(void *ptr, size_t size, size_t count, FILE *stream);ptr:指向存储数据的内存地址size:每个数据项的大小(字节)count:要读取的数据项数量stream:文件指针
返回值:成功读取的数据项数量(若小于 count,可能遇到文件结尾或错误)
示例片段:
int data[10];
size_t read_count = fread(data, sizeof(int), 10, fp);
printf("成功读取 %zu 个整数\n", read_count);2. fwrite() – 向文件写入数据块
size_t fwrite(const void *ptr, size_t size, size_t count, FILE *stream);参数与 fread 类似,用于将内存中的数据写入文件。
示例片段:
int arr[] = {1, 2, 3, 4, 5};
size_t written = fwrite(arr, sizeof(int), 5, fp);
printf("成功写入 %zu 个整数\n", written);3. 结构体读写示例
typedef struct {
char name[50];
int age;
float score;
} Student;
// 写入结构体
Student s = {"张三", 20, 89.5};
fwrite(&s, sizeof(Student), 1, fp);
// 读取结构体
Student read_s;
rewind(fp); // 回到文件开头
fread(&read_s, sizeof(Student), 1, fp);
printf("姓名: %s, 年龄: %d, 分数: %.2f\n",
read_s.name, read_s.age, read_s.score);五、文本与二进制模式的区别
1. 换行符处理
- 文本模式("r"、"w"、"a"):
\n会转换为平台相关换行符(Windows:\r\n,Unix:\n) - 二进制模式("rb"、"wb"、"ab"):无任何转换,直接读写原始字节
2. 文件末尾处理
- 文本模式可能因平台差异影响
feof()行为 - 二进制模式更适合精确控制文件读写位置
3. 使用建议
- 文本文件:使用文本模式
- 图像、音频、结构体数据:使用二进制模式
六、文件定位函数
1. ftell() - 获取当前位置
long ftell(FILE *stream);2. fseek() - 设置文件位置
int fseek(FILE *stream, long offset, int origin);origin参数:
SEEK_SET- 文件开头SEEK_CUR- 当前位置SEEK_END- 文件末尾
示例片段:
// 移动到文件开头
fseek(fp, 0, SEEK_SET);
// 移动到文件末尾
fseek(fp, 0, SEEK_END);
// 获取文件大小
long size = ftell(fp);3. rewind() - 重置到文件开头
void rewind(FILE *stream);七、错误处理函数
1. feof() - 检测文件结束
int feof(FILE *stream);2. ferror() - 检测文件错误
int ferror(FILE *stream);示例片段:
while (!feof(fp)) {
char buffer[100];
if (fgets(buffer, sizeof(buffer), fp) != NULL) {
// 处理数据
}
}
if (ferror(fp)) {
perror("读取文件时出错");
}八、实际应用示例
1. 统计文件行数
int count_lines(FILE *fp) {
int lines = 0;
int ch;
while ((ch = fgetc(fp)) != EOF) {
if (ch == '\n') lines++;
}
rewind(fp); // 重置文件指针
return lines;
}2. 文件复制
void copy_file(const char *src, const char *dest) {
FILE *src_fp = fopen(src, "r");
FILE *dest_fp = fopen(dest, "w");
if (src_fp && dest_fp) {
int ch;
while ((ch = fgetc(src_fp)) != EOF) {
fputc(ch, dest_fp);
}
}
if (src_fp) fclose(src_fp);
if (dest_fp) fclose(dest_fp);
}3. CSV文件读取
void read_csv(const char *filename) {
FILE *fp = fopen(filename, "r");
if (!fp) return;
char line[1024];
while (fgets(line, sizeof(line), fp)) {
// 去除换行符
line[strcspn(line, "\n")] = 0;
char *token = strtok(line, ",");
while (token) {
printf("%s\t", token);
token = strtok(NULL, ",");
}
printf("\n");
}
fclose(fp);
}4. 二进制文件复制(高效版)
void copy_binary_file(const char *src, const char *dest) {
FILE *src_fp = fopen(src, "rb");
FILE *dest_fp = fopen(dest, "wb");
if (!src_fp || !dest_fp) {
perror("文件打开失败");
return;
}
char buffer[4096]; // 4KB 缓冲区
size_t bytes_read;
while ((bytes_read = fread(buffer, 1, sizeof(buffer), src_fp)) > 0) {
fwrite(buffer, 1, bytes_read, dest_fp);
}
fclose(src_fp);
fclose(dest_fp);
}5. 读取二进制数据并计算校验和
unsigned int calculate_checksum(const char *filename) {
FILE *fp = fopen(filename, "rb");
if (!fp) return 0;
unsigned int sum = 0;
int byte;
while ((byte = fgetc(fp)) != EOF) {
sum += (unsigned char)byte;
}
fclose(fp);
return sum;
}6. 结构体数组读写
void save_students(const char *filename, Student *students, int count) {
FILE *fp = fopen(filename, "wb");
if (!fp) return;
fwrite(students, sizeof(Student), count, fp);
fclose(fp);
}
void load_students(const char *filename, Student *students, int max_count) {
FILE *fp = fopen(filename, "rb");
if (!fp) return;
fread(students, sizeof(Student), max_count, fp);
fclose(fp);
}九、最佳实践建议
- 始终检查返回值:所有文件操作函数都可能失败
- 及时关闭文件:避免文件描述符泄漏
- 使用缓冲区:对于大量数据,考虑使用缓冲
- 处理异常情况:考虑磁盘满、权限不足等情况
- 跨平台考虑:注意Windows和Unix的换行符差异
十、注意事项
- 文本模式 vs 二进制模式
- 文件指针的有效性检查
- 缓冲区溢出预防
- 大文件处理(考虑使用64位文件操作)
- 多线程环境下的文件访问同步
