读取文件时遇到EOF不正确

Encountered EOF incorrectly while reading file

提问人:Shepard Merose 提问时间:12/15/2022 最后编辑:Shepard Merose 更新时间:12/15/2022 访问量:168

问:

以下是我的程序。我尝试读取文件并将其数据传输到 . 很长,大约有 270 万行,但是当我运行程序并检查时;它只有 250 万行(准确地说是 2554994 行),这意味着在这一行中,程序读取到 EOF。但是,情况并非如此。我不知道出了什么问题rgb_values.txtout.txtrgb_values.txtout.txt

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

rgb_values.txt链接

我的链接 out.txt

我的输入文件的一部分:

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
C eof

评论

1赞 Some programmer dude 12/15/2022
你的功能,为什么不直接从?为什么不改用呢?并直接读入而不是临时虚拟变量?getOneBytesscanflinestrtolpt[i]x1
4赞 Some programmer dude 12/15/2022
请注意,fgetc 返回一个 int 值。当您想与 值进行比较时,这非常重要intEOF
1赞 Some programmer dude 12/15/2022
哦,请从您阅读的文件中附上一小部分样本。
1赞 Some programmer dude 12/15/2022
为什么全变量,当你只在单个函数中局部使用它时?如果需要在调用之间保留其值,至少将其设置为局部变量。countstatic
2赞 Some programmer dude 12/15/2022
最后,如果输入和输出文件的每一行都有三个值,为什么不读取整行并一次解析这三个值呢?写作也是如此。为什么要为 16 个值而烦恼,这不是 3 的倍数,这只会让代码更奇怪。如果你使用三的倍数,那么至少你可以跳过状态的变量。并且可以简化您的所有功能。count

答:

1赞 chux - Reinstate Monica 12/15/2022 #1

我不知道出了什么问题

编码不是防御性的,因为它不会寻找意外事件。

要了解出了什么问题,请改进代码的错误检测。

换句话说,不要相信用户输入——这是邪恶的。


为什么只读取 3 个字节?

下面从文件中读取最多 3 个字节,将一行的其余部分留给以后读取。

char line[4]={0x0};
fgets(line,sizeof(line),plaintext);

它不会只从文件中读取 3 个字节并抛弃该行的其余部分。

检查 fgets() 的返回值

// fgets(line,sizeof(line),plaintext);
if (fgets(line,sizeof(line),plaintext) == NULL) {
  TBD_alert();
}

使用 int

int fgetc()可以返回 257 个不同的值。储蓄会失去一些东西。char

// char ch;
int ch;

// char sh;
int sh;

缺乏错误检查

如果返回 0(无转换)怎么办?sscanf(str,"%02x ",&x1)

如果 4 太小怎么办?

如果存在额外的空格 (space, , ...),则 4 太小了。'\r'

char line[4]={0x0};
fgets(line,sizeof(line),plaintext);

相反,读取一整行,允许至少 2 倍大小的缓冲区超过预期大小,并容忍额外的空格或最后一的缺失。给定一行 ,使用缓冲区大小 (3*3 + 1)*2。fgets()'\n'"c1 c1 c1\n"

我怀疑有很多潜伏在周围。'\r'

避免UB。使用匹配说明符

"%x"匹配 ,而不是 。unsignedint

// int x1=0x0;
unsigned x1 = 0x0;  
...
 // '0' is useless, '2' is not needed.  Trailing space is useless
// sscanf(str,"%02x ",&x1);  
sscanf(str,"%x", &x1);    // Check return value - not shown

或直接保存

sscanf(str, "%hhx", &pt[i]);

每行 3 个值之外还有什么?

读取一行 3 个十六进制值并检测大量错误的示例代码。

#define EXPECTED_LINE_SIZE (3*3 + 1 /* for the \0 */)

char line[EXPECTED_LINE_SIZE * 2];
if (fgets(line, sizeof line, plaintext) == NULL) {
  return failure;
}
int n = 0;
unsigned val[3];
sscanf(line "%2x %2x %2x %n", &val[0], &val[1], &val[2], &n);
if (n == 0 || line[n] != '\0') {
  return failure;
}

检查 fopen() 是否失败

FILE *fp=fopen("rgb_values.txt","r");
FILE *fo=fopen("out.txt","w");

if (fp == NULL || fo == NULL) {
  TBD_Error_out();  // Add your code here
}

评论

0赞 Shepard Merose 12/15/2022
等于 ?TBD_alertreturn failure
0赞 chux - Reinstate Monica 12/15/2022
@ShepardMerose 一个虚构的名称,用于指示您需要编写一些待确定的代码,以提醒用户注意意外问题 - 或您认为合适的其他代码。
0赞 Some programmer dude 12/15/2022 #2

如果您只想从一个文件复制到另一个文件,请以二进制模式打开,并在循环中打开一个 4KiB 的缓冲区,然后将其复制到另一个文件。方便快捷:freadfwrite

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

int main(void)
{
    FILE *in  = fopen(..., "rb");
    FILE *out = fopen(..., "wb");

    if (!in || !out)
        return EXIT_FAILURE;

    char buffer[4096];
    size_t nread;
    while ((nread = fread(buffer, 1, sizeof buffer, in)) > 0)
        fwrite(buffer, 1, nread, out);
}

否则,如果您需要将其作为字符串读取,请在循环中使用:fgetsfputs

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

int main(void)
{
    FILE *in  = fopen(..., "r");
    FILE *out = fopen(..., "w");

    if (!in || !out)
        return EXIT_FAILURE;

    char line[256];
    while (fgets(line, sizeof line, in) != NULL)
        fputs(line, out);
}

或者正如我所提到的,要读取一整行,解析出所有三个值,传回调用函数(连同文件读取成功状态),并同时读取所有三个值:fgetssscanffprintf

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

int read_values(FILE *in, int *values)
{
    char line[256];
    if (fgets(line, sizeof line, in) == NULL)
        return 0;  // Failure to read

    if (sscanf(line("%x %x %x", &values[0], &values[1], &values[2]) != 3)
        return 0;  // Failure to parse

    return 1;  // Success
}

void write_values(FILE *out, int *values)
{
    fprintf(out, "%02x %02x %02x\n", values[0], values[1], values[2]);
}

int main(void)
{
    FILE *in  = fopen(..., "r");
    FILE *out = fopen(..., "w");

    if (!in || !out)
        return EXIT_FAILURE;

    int values[3];
    while (read_values(in, values))
        write_values(out, values);
}

请注意,我不会关闭文件。如果你的程序在此之后做了更多的事情,那么它们真的应该被关闭。如果程序刚刚退出,那么系统将为我们关闭它们。