【问题标题】:Infinite Loop While Modifying Binary File修改二进制文件时无限循环
【发布时间】:2021-05-06 17:03:21
【问题描述】:

我正在尝试打开一个二进制文件,从中读取一部分然后对其进行修改,但这最终会陷入无限循环。

在编写程序之前我的想法:

  1. EOF 是一个函数返回的值,当找到文件结尾或发生错误时,该函数从流中读取/写入流。
  2. 文件指示器将随着每次成功的读/写而向右移动它读取的字节大小。 (即如果位置指示器在 0 位置,如果在成功读/写后读/写 44 个字节,它将转到位置 44。)

下面的代码是我的许多方法之一,但没有成功:

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

struct stoixeia
{
    char e[30];
    char t[5];
    float mo;
    int ilikia;
};


int main(void)
{
    FILE *fptr;
    struct stoixeia st;
    int c;

    if((fptr = fopen("sxoleio","rb+")) == NULL)
        exit(1);

    while((c = fread(&st,sizeof(struct stoixeia),1,fptr)) != EOF)
    {
        fseek(fptr,-44,SEEK_CUR);
        for(int i = 0; i < strlen(st.e); i++)
            st.e[i] += 1;
        fwrite(&st,sizeof(struct stoixeia),1,fptr);
    }
    fclose(fptr);
    return 0;

其他不成功的方法:(feof,检查循环内的eof等)

到目前为止,我唯一的成功是使用以下代码:

    while((c = fgetc(fptr)) != EOF)
    {
        fseek(fptr,-1,SEEK_CUR);
        fread(&st,sizeof(struct stoixeia),1,fptr);
        fseek(fptr,-44,SEEK_CUR);
        for(int i = 0; i < strlen(st.e); i++)
            st.e[i] += 1;
        fwrite(&st,sizeof(struct stoixeia),1,fptr);
        fseek(fptr,1,SEEK_CUR);
        fseek(fptr,-1,SEEK_CUR);
    }
    fclose(fptr);

使用fgetc()作为while条件并在操作后将指示符向右移动一个字节然后将其向后移动一个字节似乎触发EOF并且程序以成功结束。

这种行为的原因是什么?

我真的很想了解这个行为,所以任何事情都会非常感激。

提前致谢。

【问题讨论】:

  • 成功时,fread()fwrite() 返回读取或写入的项目数。此数字等于仅当 size 为 1 时传输的字节数。如果发生错误或到达文件末尾,返回值是一个短项计数(或零)。跨度>
  • 使用-sizeof st 代替-44(也代替sizeof(struct stoixeia)) 不要使用strlen(读取可能没有正确地以null 终止字符串,而是使用for( size_t i = 0; st.e[i] &amp;&amp; i &lt; sizeof st.e; i++ ){。将fread 的结果与 1 而不是 EOF 进行比较

标签: c file binaryfiles


【解决方案1】:

您的代码存在一些问题,但我认为最大的误解是fread 的返回值。不应将其返回值与 EOF 进行比较。当您到达文件末尾时,fread 将返回一个简短的计数。在这种情况下,这意味着它将返回零。试试:

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

struct stoixeia {
    char e[30];
    char t[5];
    float mo;
    int ilikia;
};

static void die(const char *msg) { perror(msg); exit(EXIT_FAILURE); }
int
main(int argc, char **argv)
{
        FILE *fptr;
        struct stoixeia st;
        size_t c;
        const char *path = argc > 1 ? argv[1] : "sxoleio";

        if( (fptr = fopen(path, "rb+")) == NULL ){
                die(path);
        }
        while( (c = fread(&st, sizeof st, 1, fptr)) == 1 ){
                if( fseek(fptr, -sizeof st, SEEK_CUR) == -1 ){
                        die("fseek");
                }
                for( size_t i = 0; st.e[i] && i < sizeof st.e; i++ ){
                        st.e[i] += 1;
                }
                if( fwrite(&st, sizeof st, 1, fptr) != 1 ){
                        die("fwrite");
                };
        }
        if( fclose(fptr) ){
                die("fclose");
        }
        return EXIT_SUCCESS;
}

但是,完全跳过搜索并使用两个不同的文件句柄可能更干净。比如:

if( (rf = fopen(path, "r")) == NULL) {
        die(path);
}
if( (wf = fopen(path, "r+")) == NULL) {
        die(path);
}
while( (c = fread(&st, sizeof st, 1, rf)) == 1) {
        for( size_t i = 0; st.e[i] && i < sizeof st.e; i++ ){
                st.e[i] += 1;
        }
        if( fwrite(&st, sizeof st, 1, wf) != 1 ){
                die("fwrite");
        };
}

【讨论】:

  • 我尝试了您的第一种方法,但它再次陷入无限循环,所以我尝试了第二种方法,它按预期工作。关于我在某处读到的行为,调用 fseek() 的副作用是文件上的 EOF 指示,如果它是一个成功的调用,那么由于文件是用 "rb+" 这意味着 fseek 将永远成功,所以也许是这样? @William Pursell
  • @SpirosGkogkas 我认为在第一种情况下您没有无限循环。我怀疑正在发生的是输入文件末尾有一些数据(它的大小不是结构大小的倍数)并且您正在阻止读取。尝试将循环更改为 while( fread(&amp;st + i++, 1, 1, fptr)) == sizeof st ){ 或类似内容以处理最终的短读。
  • 注意,我之前的评论只有在您不是从常规文件(例如,命名管道)中读取时才有意义。你如何确定你有一个无限循环?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-07
  • 2017-01-09
  • 2014-11-28
  • 2015-09-06
相关资源
最近更新 更多