C语言中scanf/fopen安全读取文本文件至数组的问题排查
问题分析与解决
问题描述
输入为:5 1 2 3 4 5 6,期望输出为:10 2 4 6 8 10 12。需要读取未知数量的输入数字并存入数组arr中,编写的代码出现无限循环,询问原因及读取逻辑是否正确。代码如下:
#define MAX 100 #include <stdlib.h> #include <stdio.h> #include <assert.h> int main() { // ....................read data........................ FILE* inp = fopen("input.txt", "r"); FILE* out = fopen("output.txt", "w"); if (inp == NULL) { fprintf(stderr, "Unable to reopen stdin. "); return EXIT_FAILURE; } if (out == NULL) { fclose(inp); fprintf(stderr, "Unable to reopen stdout. "); return EXIT_FAILURE; } int a, n = MAX, i = 0; //int* arr = malloc(sizeof * arr * length); int* arr = calloc(n, sizeof * arr); for (;;) { if (fscanf(inp,"%d", &a) != 1) { fprintf(stderr, "Failed to read an int. "); break; } else { arr[i] = a; fprintf(stdout, "%d ", arr[i] * 2); i++; } } // ....................close - free........................ free(arr); fclose(inp); fclose(out); return 0; }
无限循环原因
当fscanf(inp, "%d", &a)读取失败时(比如遇到非数字字符、文件结束符,或者输入格式不匹配),无效的输入内容会留在输入缓冲区中。下一次循环时,fscanf会再次尝试读取这个无效内容,依然失败,导致循环无法终止,进入无限循环。
另外,原代码中错误提示字符串的换行符写法错误("Unable to reopen stdin. "),应该改为"Unable to reopen stdin.\n",否则会导致编译警告或输出异常。
读取逻辑的问题
- 数组越界风险:用固定大小
MAX=100的数组存储输入,但需求是读取未知数量的数字。如果输入数字超过100个,i会超过数组长度,触发未定义行为(比如程序崩溃)。 - 输出目标错误:代码中打开了
output.txt文件,但实际输出是写到stdout(控制台),没有写入目标文件,不符合需求。 - 缺乏动态扩容:固定数组大小无法满足“未知数量输入”的需求,需要动态调整数组容量。
修正后的代码
#include <stdlib.h> #include <stdio.h> #define INIT_CAPACITY 10 // 初始数组容量,可根据需求调整 int main() { // 打开输入输出文件 FILE* inp = fopen("input.txt", "r"); FILE* out = fopen("output.txt", "w"); if (inp == NULL) { fprintf(stderr, "无法打开输入文件 input.txt\n"); return EXIT_FAILURE; } if (out == NULL) { fclose(inp); fprintf(stderr, "无法打开输出文件 output.txt\n"); return EXIT_FAILURE; } int* arr = malloc(INIT_CAPACITY * sizeof(int)); if (arr == NULL) { fprintf(stderr, "内存分配失败\n"); fclose(inp); fclose(out); return EXIT_FAILURE; } int a, count = 0; size_t capacity = INIT_CAPACITY; for (;;) { int ret = fscanf(inp, "%d", &a); if (ret == 1) { // 检查数组容量,不足则扩容 if (count >= capacity) { size_t new_capacity = capacity * 2; int* temp = realloc(arr, new_capacity * sizeof(int)); if (temp == NULL) { fprintf(stderr, "数组扩容失败\n"); free(arr); fclose(inp); fclose(out); return EXIT_FAILURE; } arr = temp; capacity = new_capacity; } arr[count] = a; fprintf(out, "%d ", arr[count] * 2); count++; } else if (ret == EOF) { // 文件读取完毕,正常退出循环 break; } else { // 读取到无效输入,清理缓冲区 fprintf(stderr, "读取整数失败,跳过无效字符\n"); int c; while ((c = fgetc(inp)) != EOF && c != '\n') { // 跳过当前行的无效字符 } // 如果是文件结束,退出循环 if (c == EOF) break; } } // 释放资源 free(arr); fclose(inp); fclose(out); return 0; }
修正说明
- 处理输入失败的情况:当
fscanf返回非1且非EOF时,用fgetc跳过当前行的无效字符,避免无限循环。 - 动态数组扩容:初始设置较小的数组容量,当元素数量超过容量时,用
realloc将容量翻倍,满足未知数量输入的需求。 - 修正输出目标:将输出从
stdout改为写入output.txt文件。 - 完善错误处理:增加内存分配、扩容的失败检查,确保代码健壮性。
- 修正字符串换行符:统一使用
\n作为换行符,避免编译和输出异常。
内容的提问来源于stack exchange,提问作者v_head
相关产品推荐
相关产品推荐

