【问题标题】:Reading a binary file 1 byte at a time一次读取 1 个字节的二进制文件
【发布时间】:2011-08-30 21:30:50
【问题描述】:

我试图一次读取一个 C 1 字节的二进制文件,在互联网上搜索了几个小时后,除了垃圾和/或段错误之外,我仍然无法检索任何内容。基本上,二进制文件的格式是一个有 256 个项目长的列表,每个项目是 1 个字节(0 到 255 之间的无符号整数)。我正在尝试使用 fseek 和 fread 跳转到二进制文件中的“索引”并检索该值。我目前拥有的代码:

unsigned int buffer;

int index = 3; // any index value

size_t indexOffset = 256 * index;
fseek(file, indexOffset, SEEK_SET);
fread(&buffer, 256, 1, file);

printf("%d\n", buffer);

现在这段代码给了我随机的垃圾号码和段错误。关于如何让它正常工作的任何提示?

【问题讨论】:

  • 一次读取一个字节的文件会产生大量的 I/O 开销。更好的方法是将整个文件读入缓冲区(因为它只有 256 个字节)并单独处理存储在缓冲区中的每个字节。
  • @titaniumdecoy - 我相信 I/O 管理器会缓冲现代操作系统上的读取。

标签: c file io binary


【解决方案1】:

您正试图将 256 个字节读入一个名为“缓冲区”的 4 字节整数变量。您正在覆盖接下来的 252 个字节的其他数据。

似乎buffer 应该是unsigned char buffer[256]; 或者您应该使用fread(&buffer, 1, 1, f),在这种情况下buffer 应该是unsigned char buffer;。

或者,如果您只想要一个字符,您可以将 buffer 保留为 int(不需要 unsigned,因为 C99 保证了普通 int 的合理最小范围)并简单地说:

buffer = fgetc(f);

【讨论】:

    【解决方案2】:

    目前的代码存在几个问题。

    fread 的原型是:

    size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
    

    您已将大小设置为 256(字节)并将计数设置为 1。这很好,这意味着“读取一个 256 字节的块,将其推入缓冲区”。

    但是,您的缓冲区大约为 2-8 个字节长(或者,至少远小于 256 个字节),因此您的缓冲区溢出。您可能想使用 fred(&buffer, 1, 1, file)。

    此外,您正在将字节数据写入 int 指针。这将适用于一种字节序(实际上是小字节序),因此您在英特尔架构上会很好,并从中学习坏习惯,这些习惯会回来咬您,其中一个。

    尽量只将字节数据写入按字节组织的存储,而不是写入整数或浮点数。

    【讨论】:

      【解决方案3】:

      你把 bytes 和 int 混淆了。 byte 的常用术语是 unsigned char。大多数字节为 8 位宽。如果你正在读取的数据是 8 位,则需要读取 8 位:

      #define BUFFER_SIZE 256
      
      unsigned char buffer[BUFFER_SIZE];
      
      /* Read in 256 8-bit numbers into the buffer */
      size_t bytes_read = 0;
      bytes_read = fread(buffer, sizeof(unsigned char), BUFFER_SIZE, file_ptr);
      // Note: sizeof(unsigned char) is for emphasis
      

      将所有数据读入内存的原因是为了保持 I/O 流畅。无论请求的数量如何,每个输入请求都会产生开销。一次读取一个字节,或一次寻找一个位置是最坏的情况。

      以下是读取 1 个字节所需开销的示例:

      Tell OS to read from the file.
      OS searches to find the file location.
      OS tells disk drive to power up.
      OS waits for disk drive to get up to speed.
      OS tells disk drive to position to the correct track and sector.
      -->OS tells disk to read one byte and put into drive buffer.
      OS fetches data from drive buffer.
      Disk spins down to a stop.
      OS returns 1 byte to your program.
      

      在您的程序设计中,上述步骤将重复 256 次。按照大家的建议,标有“-->”的那行读256字节。因此开销只执行一次而不是 256 次以获得相同数量的数据。

      【讨论】:

      • 有人告诉我操作系统实际上会进行缓冲。无论如何,如果不是,我很确定光盘控制器会进行一点缓冲。
      • 是的,但是总有调用输入函数的开销指令;可能包括重新加载指令缓存。为了提高性能,应该减少这种开销 - 因此每个请求更多的数据更有效。
      • 如果文件只有 255 字节,而您尝试读取 256 字节会发生什么?你可以循环遍历bytes_read 来读取整个文件吗?
      • 如果你要求256字节但只有253字节,fread函数会返回253。你可以读取多个blocks,直到读取整个文件;由于有限的小内存大小,这是一种古老的艺术。如果您正在读取文件,我建议您使用 512 或 1024 的倍数,因为这是扇区的大小。
      【解决方案4】:
      unsigned char buffer; // note: 1 byte
      fread(&buffer, 1, 1, file);
      

      我相信是时候阅读男人了。

      【讨论】:

        【解决方案5】:

        在您的代码中,您尝试将 256 个字节读取到一个 int 的地址。如果您想一次读取一个字节,请致电fread(&buffer, 1, 1, file);(请参阅fread)。

        但更简单的解决方案是声明一个字节数组,将其全部读取并在之后进行处理。

        【讨论】:

        • 缓冲区是无符号整数类型。未初始化,因此在您的fread(&buffer, 1, 1, file); 之后,您将获得一个数字,其中 24 位将包含一些随机值。
        • 如何声明一个字节数组并一起读取?谢谢!
        • @tommy.carstensen - 看看我下面的答案:)
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-12-06
        • 2020-11-20
        • 2012-07-11
        • 2010-11-05
        • 2017-04-14
        • 1970-01-01
        • 2015-12-11
        相关资源
        最近更新 更多