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

读取大文件时提前触发EOF致数据丢失的技术求助

问题描述

我编写了一段C语言程序,用于读取rgb_values.txt文件(约270万行)并将数据转存至out.txt。但运行程序后发现,out.txt仅生成了2554994行,程序提前读取到了EOF,与预期不符,无法定位问题所在。

程序代码

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include <stdio.h>
int count=0;
void getOneByte(FILE *plaintext,unsigned char *pt,int i){
    char line[4]={0x0};
    fgets(line,sizeof(line),plaintext);
    int x1=0x0;
    char str[4];
    sprintf(str,"%s",line);
    sscanf(str,"%02x ",&x1);
    pt[i]=x1;
}
void get16Bytes(FILE *plaintext,unsigned char * pt){
    int i=0;
    char sh;
    for (int i = 0; i <=15; i++)
    {
        getOneByte(plaintext,pt,i);
        if((sh=fgetc(plaintext))!=EOF){
            ungetc(sh,plaintext);
        }
        else{
            ungetc(sh,plaintext);
            break;
        }
    }
}

void write16Bytes(FILE *ciphertext,unsigned char *ct){
    int i;
    for(i = 0; i < 16; i++){
        if(count == 0){
            fprintf(ciphertext,"%02x",ct[i]);
            count++;
        }
        else if(count == 1){
            fprintf(ciphertext," %02x",ct[i]);
            count++;
        }
        else if(count == 2){
            fprintf(ciphertext," %02x\n",ct[i]);
            count = 0;
        }
    }  
}
int main(){
    FILE *fp=fopen("rgb_values.txt","r");
    FILE *fo=fopen("out.txt","w");
    char ch;
    unsigned char pt[16];
    int i=15;
    int cnt=0;
    while(1){
        memset(pt,'a',sizeof(pt));
        get16Bytes(fp,pt);
        if((ch=fgetc(fp))!=EOF){
            ungetc(ch,fp);
        }
        else{
            break;
        }
        write16Bytes(fo,pt);
        cnt++;
    }
    fclose(fp);
    return 1;
}

输入文件片段

c1 c1 c1
ff ff ff
ff ff ff
ff ff ff
ff ff ff
fe fe fe
fd fd fd
ff ff ff
fb fb fb
fe fe fe
fe fe fe
ff ff ff
fb fb fb
f1 f1 f1
e9 e9 e9
e6 e6 e6
e5 e5 e5
e5 e5 e5
e5 e5 e5
e5 e5 e5
e5 e5 e5
e5 e5 e5
e5 e5 e5
e5 e5 e5
e5 e5 e5
e5 e5 e5
e5 e5 e5
e2 e2 e2
e5 e5 e5
e7 e7 e7
e5 e5 e5
e4 e4 e4
e3 e3 e3
e3 e3 e3
e8 e8 e8
de de de
9c 9c 9c
3b 3b 3b
01 01 01
02 02 02
00 00 00
02 02 02
0a 0a 0a
问题分析与修复

核心问题

  1. fgets读取限制导致解析失败:getOneByte中fgets的缓冲区长度为4,当输入中的十六进制值与换行符组合时,可能出现读取不完整的情况,引发sscanf解析错误,进而导致后续EOF判断异常。
  2. EOF处理的未定义行为:get16Bytes中对EOF执行ungetc操作是C标准未定义的行为,会破坏文件流状态,导致程序提前终止循环。
  3. 主循环冗余判断丢失数据:主循环在get16Bytes后额外读取字符判断EOF,会导致最后一组有效数据因触发EOF而被跳过写入。
  4. 全局变量count状态混乱:全局变量在多次调用write16Bytes时易出现状态异常,影响输出格式的正确性。

修复后的代码

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<ctype.h>

// 静态变量替代全局变量,减少全局空间污染
static int line_col = 0;

// 读取单个十六进制值,自动跳过空白字符
int read_hex(FILE *fp) {
    int val;
    // 跳过所有空白字符(空格、换行、制表符等)
    while (1) {
        int ch = fgetc(fp);
        if (ch == EOF) return -1;
        if (!isspace((unsigned char)ch)) {
            ungetc(ch, fp);
            break;
        }
    }
    // 读取2位十六进制值
    if (fscanf(fp, "%2x", &val) != 1) {
        return -1;
    }
    return val;
}

// 读取16个十六进制值到缓冲区
void read_16_vals(FILE *fp, unsigned char *buf) {
    int i;
    for (i = 0; i < 16; i++) {
        int val = read_hex(fp);
        if (val == -1) break;
        buf[i] = (unsigned char)val;
    }
    // 填充剩余位置为0(按需调整)
    for (; i < 16; i++) {
        buf[i] = 0;
    }
}

// 将16个值按格式写入文件
void write_16_vals(FILE *fp, unsigned char *buf) {
    for (int i = 0; i < 16; i++) {
        if (line_col == 0) {
            fprintf(fp, "%02x", buf[i]);
            line_col = 1;
        } else if (line_col == 1) {
            fprintf(fp, " %02x", buf[i]);
            line_col = 2;
        } else {
            fprintf(fp, " %02x\n", buf[i]);
            line_col = 0;
        }
    }
}

int main() {
    FILE *fp_in = fopen("rgb_values.txt", "r");
    FILE *fp_out = fopen("out.txt", "w");
    if (!fp_in || !fp_out) {
        perror("文件打开失败");
        return EXIT_FAILURE;
    }

    unsigned char buf[16];
    int has_data;

    do {
        memset(buf, 0, sizeof(buf));
        read_16_vals(fp_in, buf);

        // 检查是否读取到有效数据
        has_data = 0;
        for (int i = 0; i < 16; i++) {
            if (buf[i] != 0) { // 若原文件存在0值,需调整判断逻辑
                has_data = 1;
                break;
            }
        }

        if (has_data) {
            write_16_vals(fp_out, buf);
        }
    } while (has_data);

    fclose(fp_in);
    fclose(fp_out);
    return EXIT_SUCCESS;
}

关键优化点

  • 使用fscanf直接读取十六进制值,自动跳过空白字符,避免fgets的长度限制问题。
  • 修复EOF处理逻辑,避免未定义行为。
  • 调整主循环终止条件,确保最后一组有效数据被写入。
  • 用静态变量替代全局变量,减少代码耦合。
  • 添加文件打开失败的错误处理,提升程序健壮性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 04:45:41