C语言文件操作实战:3种模式读写文本与二进制文件(附完整代码)
C语言文件操作实战3种模式读写文本与二进制文件附完整代码在软件开发中文件操作是不可或缺的基础技能。无论是保存用户配置、记录日志还是处理大规模数据都需要通过文件系统进行持久化存储。C语言作为系统级编程语言提供了一套高效而灵活的文件操作接口掌握这些技能将极大提升你的编程能力。1. 文件操作基础与模式选择1.1 文件指针与打开方式C语言通过FILE结构体指针来操作文件这个指针包含了文件的所有状态信息。使用fopen函数打开文件时需要指定访问模式这决定了后续能进行的操作类型。FILE *fp fopen(example.txt, r); // 只读模式打开文本文件 if (fp NULL) { perror(文件打开失败); return EXIT_FAILURE; }常见的文件打开模式包括模式描述文件存在文件不存在r只读打开成功打开失败w只写清空内容创建新文件a追加保留内容创建新文件r读写保留内容打开失败w读写清空内容创建新文件a读写保留内容创建新文件提示处理二进制文件时在模式字符串后添加b如rb、wb等。在Windows系统中这可以防止换行符转换问题。1.2 文本文件与二进制文件的本质区别文本文件和二进制文件在物理存储上都是二进制数据区别在于解释方式文本文件由可打印字符组成按行组织每行以换行符结束二进制文件直接存储内存中的原始数据没有特殊格式要求关键差异对比特性文本文件二进制文件可读性可直接阅读需要特定程序解析大小通常较大通常较小处理速度较慢较快数据精度可能丢失完全保留跨平台需处理换行符差异无此问题2. 文本文件操作实战2.1 读写基本示例下面是一个完整的文本文件读写示例展示三种基本操作模式#include stdio.h #include stdlib.h void text_file_demo() { // 写入模式示例 FILE *fp fopen(demo.txt, w); if (fp) { fputs(第一行内容\n, fp); fprintf(fp, 格式化写入: %d %.2f\n, 42, 3.14); fclose(fp); } // 追加模式示例 fp fopen(demo.txt, a); if (fp) { fputs(这是追加的内容\n, fp); fclose(fp); } // 读取模式示例 fp fopen(demo.txt, r); if (fp) { char buffer[256]; printf(文件内容:\n); while (fgets(buffer, sizeof(buffer), fp)) { printf(%s, buffer); } fclose(fp); } }2.2 行处理技巧处理文本文件时经常需要逐行读取内容。以下是一些实用技巧// 安全读取行并移除换行符 char* safe_fgets(char *buf, int size, FILE *fp) { if (fgets(buf, size, fp)) { buf[strcspn(buf, \n)] \0; // 移除换行符 return buf; } return NULL; } // 逐行处理文件 void process_lines(const char *filename) { FILE *fp fopen(filename, r); if (!fp) return; char line[1024]; while (safe_fgets(line, sizeof(line), fp)) { // 处理每一行内容 printf(处理行: %s\n, line); } fclose(fp); }3. 二进制文件操作进阶3.1 结构体序列化二进制文件最常见的用途是存储结构化数据。以下示例展示如何将结构体写入文件并读取typedef struct { int id; char name[50]; float score; } Student; void binary_file_demo() { Student students[3] { {1, 张三, 89.5}, {2, 李四, 92.0}, {3, 王五, 78.5} }; // 写入二进制文件 FILE *fp fopen(students.dat, wb); if (fp) { fwrite(students, sizeof(Student), 3, fp); fclose(fp); } // 读取二进制文件 Student read_students[3]; fp fopen(students.dat, rb); if (fp) { size_t count fread(read_students, sizeof(Student), 3, fp); printf(读取到 %zu 条记录:\n, count); for (int i 0; i count; i) { printf(ID: %d, 姓名: %s, 分数: %.1f\n, read_students[i].id, read_students[i].name, read_students[i].score); } fclose(fp); } }3.2 数据块操作技巧对于大型数据分块读写可以提高效率并减少内存占用#define CHUNK_SIZE 1024 void file_copy(const char *src, const char *dest) { FILE *src_fp fopen(src, rb); FILE *dest_fp fopen(dest, wb); if (!src_fp || !dest_fp) { perror(文件打开失败); if (src_fp) fclose(src_fp); if (dest_fp) fclose(dest_fp); return; } unsigned char buffer[CHUNK_SIZE]; size_t bytes_read; while ((bytes_read fread(buffer, 1, CHUNK_SIZE, src_fp)) 0) { fwrite(buffer, 1, bytes_read, dest_fp); } fclose(src_fp); fclose(dest_fp); }4. 高级文件操作技术4.1 随机访问与定位C语言允许在文件中随机定位这对于大型文件处理非常有用void random_access_demo() { FILE *fp fopen(data.bin, wb); if (!fp) return; // 写入测试数据 for (int i 0; i 10; i) { fwrite(i, sizeof(int), 1, fp); } // 读取特定位置数据 int value; fseek(fp, 5 * sizeof(int), SEEK_SET); // 定位到第6个整数 fread(value, sizeof(int), 1, fp); printf(第6个整数: %d\n, value); // 修改中间数据 value 100; fseek(fp, 3 * sizeof(int), SEEK_SET); fwrite(value, sizeof(int), 1, fp); // 验证修改 fseek(fp, 3 * sizeof(int), SEEK_SET); fread(value, sizeof(int), 1, fp); printf(修改后的第4个整数: %d\n, value); fclose(fp); }4.2 错误处理与状态检查健壮的文件操作需要完善的错误处理void safe_file_operations() { FILE *fp fopen(important.dat, rb); if (!fp) { perror(无法打开文件); return; } // 检查文件状态 if (feof(fp)) { printf(已到达文件末尾\n); } if (ferror(fp)) { printf(文件操作发生错误\n); clearerr(fp); // 清除错误标志 } // 获取文件大小 fseek(fp, 0, SEEK_END); long file_size ftell(fp); rewind(fp); printf(文件大小: %ld 字节\n, file_size); // 确保写入操作完成 if (fflush(fp) EOF) { perror(刷新缓冲区失败); } fclose(fp); }5. 性能优化与最佳实践5.1 缓冲策略优化文件I/O性能很大程度上受缓冲策略影响void buffering_demo() { FILE *fp fopen(large_file.dat, wb); if (!fp) return; // 设置自定义缓冲区 char buffer[8192]; if (setvbuf(fp, buffer, _IOFBF, sizeof(buffer)) ! 0) { perror(设置缓冲区失败); } // 大量小写入操作将受益于缓冲区 for (int i 0; i 10000; i) { fwrite(i, sizeof(int), 1, fp); } fclose(fp); }5.2 跨平台兼容性处理不同平台的文件系统有差异需要注意void cross_platform_demo() { // 路径分隔符处理 const char *path data #ifdef _WIN32 \\ #else / #endif file.txt; // 文本模式换行符处理 FILE *fp fopen(path, w); if (fp) { // 显式写入换行符 fputs(第一行\n, fp); fputs(第二行\r\n, fp); // Windows风格 fclose(fp); } // 二进制模式可避免换行符转换 fp fopen(path, rb); if (fp) { // 直接读取原始内容 char ch; while (fread(ch, 1, 1, fp) 1) { printf(%02X , (unsigned char)ch); } fclose(fp); } }在实际项目中文件操作往往需要处理各种边界条件和异常情况。一个健壮的文件处理模块应该包含完善的错误检查、资源释放和恢复机制。掌握这些技巧后你将能够开发出高效可靠的文件处理功能。