You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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;
}

修正说明

  1. 处理输入失败的情况:当fscanf返回非1且非EOF时,用fgetc跳过当前行的无效字符,避免无限循环。
  2. 动态数组扩容:初始设置较小的数组容量,当元素数量超过容量时,用realloc将容量翻倍,满足未知数量输入的需求。
  3. 修正输出目标:将输出从stdout改为写入output.txt文件。
  4. 完善错误处理:增加内存分配、扩容的失败检查,确保代码健壮性。
  5. 修正字符串换行符:统一使用\n作为换行符,避免编译和输出异常。

内容的提问来源于stack exchange,提问作者v_head

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 12:51:56