1
0

c_file_io.c 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. /*
  2. * ============================================================
  3. * C语言文件读写示例 (c_file_io.c)
  4. * ============================================================
  5. *
  6. * C语言使用标准库 <stdio.h> 中提供的一组函数来操作文件,
  7. * 核心是 FILE* 这个"文件指针"(其实是一个不透明的结构体指针,
  8. * 内部保存了文件描述符、缓冲区、读写位置等信息)。
  9. *
  10. * 常用函数:
  11. * FILE* fopen(const char *path, const char *mode); // 打开文件
  12. * int fclose(FILE *fp); // 关闭文件
  13. * int fprintf(FILE *fp, const char *fmt, ...); // 格式化写入(文本)
  14. * int fscanf(FILE *fp, const char *fmt, ...); // 格式化读取(文本)
  15. * char* fgets(char *buf, int n, FILE *fp); // 按行读取文本
  16. * size_t fwrite(const void *ptr, size_t size, size_t n, FILE *fp); // 二进制写
  17. * size_t fread(void *ptr, size_t size, size_t n, FILE *fp); // 二进制读
  18. * int feof(FILE *fp); // 是否到达文件末尾
  19. * int ferror(FILE *fp); // 是否发生错误
  20. *
  21. * 打开模式 mode 常见取值:
  22. * "r" 只读,文件必须存在
  23. * "w" 只写,文件不存在则创建,存在则清空
  24. * "a" 追加写,文件不存在则创建
  25. * "r+" 读写,文件必须存在
  26. * "w+" 读写,文件不存在则创建,存在则清空
  27. * "a+" 读写追加
  28. * 在 mode 后加 "b" 表示以二进制模式打开,如 "rb"、"wb"
  29. * (在 Windows 上文本模式会自动转换 \n <-> \r\n,二进制模式不转换)
  30. *
  31. * 特点总结:
  32. * 1. 面向过程,需要手动检查返回值判断是否成功(没有异常机制)。
  33. * 2. 必须显式调用 fclose 关闭文件,否则可能造成资源泄漏或数据未刷新到磁盘。
  34. * 3. 类型不安全:fprintf/fscanf 依赖格式化字符串 %d/%s 等,容易出错。
  35. * 4. 性能高、历史悠久,几乎所有C库、嵌入式系统都在用。
  36. * ============================================================
  37. */
  38. #include <stdio.h> // 标准输入输出:fopen/fclose/fprintf/fscanf/fread/fwrite等
  39. #include <stdlib.h> // exit, EXIT_FAILURE
  40. #include <string.h> // strlen
  41. /* 定义一个简单的结构体,用于演示二进制读写 */
  42. typedef struct {
  43. int id;
  44. char name[32];
  45. double score;
  46. } Student;
  47. int main(void)
  48. {
  49. const char *text_file = "c_demo.txt";
  50. const char *binary_file = "c_demo.bin";
  51. /* ---------------------------------------------------------
  52. * 第一部分:文本文件的写入与读取
  53. * --------------------------------------------------------- */
  54. printf("===== [C语言] 文本文件读写演示 =====\n");
  55. /* 1. 打开文件用于写入("w" 模式:不存在则创建,存在则清空) */
  56. FILE *fp = fopen(text_file, "w");
  57. if (fp == NULL) {
  58. /* fopen 失败时返回 NULL,必须手动判断 */
  59. perror("fopen 写入失败");
  60. return EXIT_FAILURE;
  61. }
  62. /* 2. 使用 fprintf 按格式化文本写入,用法类似 printf,只是多了一个 FILE* 参数 */
  63. fprintf(fp, "Hello from C!\n");
  64. fprintf(fp, "%d,%s,%.2f\n", 1, "Alice", 95.5);
  65. fprintf(fp, "%d,%s,%.2f\n", 2, "Bob", 88.0);
  66. /* 3. 用完必须关闭,fclose 会把缓冲区数据刷新(flush)到磁盘,并释放FILE*资源 */
  67. fclose(fp);
  68. printf("已写入文件: %s\n", text_file);
  69. /* 4. 重新打开文件用于读取 */
  70. fp = fopen(text_file, "r");
  71. if (fp == NULL) {
  72. perror("fopen 读取失败");
  73. return EXIT_FAILURE;
  74. }
  75. /* 5. 使用 fgets 按行读取文本,最多读取 sizeof(line)-1 个字符,会保留末尾的'\n' */
  76. char line[128];
  77. printf("--- 按行读取内容 ---\n");
  78. while (fgets(line, sizeof(line), fp) != NULL) {
  79. printf("读到一行: %s", line); // line里已经带了换行符
  80. }
  81. /* 判断循环结束的原因:是正常到达文件末尾,还是发生了读取错误 */
  82. if (ferror(fp)) {
  83. perror("读取过程中发生错误");
  84. }
  85. fclose(fp);
  86. /* 6. 也可以用 fscanf 按格式解析文本(适合读取结构化数据,例如 "id,name,score") */
  87. fp = fopen(text_file, "r");
  88. if (fp != NULL) {
  89. char first_line[64];
  90. fgets(first_line, sizeof(first_line), fp); // 跳过第一行 "Hello from C!"
  91. int id; char name[32]; double score;
  92. printf("--- 使用fscanf解析结构化数据 ---\n");
  93. while (fscanf(fp, "%d,%31[^,],%lf\n", &id, name, &score) == 3) {
  94. printf("解析结果: id=%d, name=%s, score=%.2f\n", id, name, score);
  95. }
  96. fclose(fp);
  97. }
  98. /* ---------------------------------------------------------
  99. * 第二部分:二进制文件的写入与读取(更高效,不做文本转换/格式化)
  100. * --------------------------------------------------------- */
  101. printf("\n===== [C语言] 二进制文件读写演示 =====\n");
  102. Student students_out[2] = {
  103. {1, "Alice", 95.5},
  104. {2, "Bob", 88.0}
  105. };
  106. /* 1. 以二进制写模式打开 "wb" */
  107. fp = fopen(binary_file, "wb");
  108. if (fp == NULL) {
  109. perror("fopen 二进制写入失败");
  110. return EXIT_FAILURE;
  111. }
  112. /* 2. fwrite(数据指针, 每个元素大小, 元素个数, 文件指针)
  113. * 直接把内存中的字节按原样写入文件,速度快,但不同平台字节序/结构体对齐可能不同,
  114. * 不适合跨平台/跨编译器长期保存数据。
  115. */
  116. size_t written = fwrite(students_out, sizeof(Student), 2, fp);
  117. printf("写入了 %zu 条学生记录\n", written);
  118. fclose(fp);
  119. /* 3. 以二进制读模式打开 "rb" 并读回 */
  120. fp = fopen(binary_file, "rb");
  121. if (fp == NULL) {
  122. perror("fopen 二进制读取失败");
  123. return EXIT_FAILURE;
  124. }
  125. Student students_in[2];
  126. size_t read_count = fread(students_in, sizeof(Student), 2, fp);
  127. printf("读取了 %zu 条学生记录:\n", read_count);
  128. for (size_t i = 0; i < read_count; i++) {
  129. printf(" id=%d, name=%s, score=%.2f\n",
  130. students_in[i].id, students_in[i].name, students_in[i].score);
  131. }
  132. fclose(fp);
  133. /* ---------------------------------------------------------
  134. * 第三部分:追加写入演示 ("a" 模式)
  135. * --------------------------------------------------------- */
  136. printf("\n===== [C语言] 追加写入演示 =====\n");
  137. fp = fopen(text_file, "a"); // 追加模式,写入内容会加到文件末尾,不会清空原内容
  138. if (fp != NULL) {
  139. fprintf(fp, "%d,%s,%.2f\n", 3, "Charlie", 77.0);
  140. fclose(fp);
  141. printf("已追加一行到 %s\n", text_file);
  142. }
  143. printf("\nC语言文件读写演示结束。\n");
  144. return 0;
  145. }