【问题标题】:How do I properly read and write Binary files? (C++)如何正确读写二进制文件? (C++)
【发布时间】:2019-10-03 21:13:12
【问题描述】:

在这个程序中,我正在读取、操作和编写二进制位图文件。我正在尝试使用 C 样式文件来读取和写入这些文件。我还使用 new() 动态地将内存分配到堆上并删除,以便将该内存块写入另一个文件。 我认为当我在 for 循环中读取和写入二进制文件时遇到问题。在查看了我的输出文件后,我不知何故被 File 和 Info 标头覆盖。我只需要帮助理解为什么会这样。我所有的其他代码都应该没问题。

#include <cstdint>
#include <cstdio>

#pragma pack(push, 2)
struct BitmapFileHeader {

    uint16_t type;
    uint32_t size;
    uint16_t reserved_1;
    uint16_t reserved_2;
    uint32_t offset;

};
struct BitmapInfoHeader {

    uint32_t size;
    uint32_t width;
    uint32_t height;
    uint16_t planes;
    uint16_t bitcount;
    uint32_t compression;
    uint32_t imagesize;
    uint32_t x_pixels_per_meter;
    uint32_t y_pixels_per_meter;
    uint32_t color_used;
    uint32_t color_important;
};
#pragma pack(pop)

struct Pixel {
    uint8_t blue;
    uint8_t green;
    uint8_t red;
};

int main(int argc, char* argv[])
{
    if(argc != 3) {
        printf("Usage : %s input_file output_file\n", argv[0]);
        return 1;
    }

    FILE *fin;
    FILE *fout;
    BitmapFileHeader bfh;
    BitmapInfoHeader bih;

    fin = fopen(argv[1], "rb");

    if (nullptr == fin) {
        perror(argv[1]);
        return -1;
    }

    if (sizeof(BitmapFileHeader) != fread(
        &bfh,
        1,
        sizeof(bfh),
        fin
    )) {
        printf("Unable to read bitmap file header. \n");
        return -2;
    }

    if (sizeof(BitmapInfoHeader) != fread(
        &bih,
        1,
        sizeof(bih),
        fin
    )) {
        printf("Unable to read bitmap info header. \n");
        return -3;
    }

    printf("Size of File Header = %lu\n", sizeof(BitmapFileHeader));

    int8_t first = (bfh.type >> 8) & 0xff;
    int8_t second = bfh.type & 0xff;

    if ( (first != 'M') && (second != 'B') ){
        printf("Input file is not a Bitmap file. \n");
        return -4;
    }

    printf("File type = %c%c\n", first, second);
    printf("File size = %u\n", bfh.size);
    printf("File offset = %u\n", bfh.offset);
    printf("File width = %u\n", bih.width);
    printf("Info size = %u\n", bih.size);

    uint32_t padding_bytes = 0;
    uint32_t row_bytes_final = bih.width * sizeof(Pixel);
    uint32_t row_bytes_initial = row_bytes_final;

    do{
        uint32_t rem = row_bytes_final % 4;

        if (rem != 0) {
            row_bytes_final += 1;
        }

        padding_bytes = row_bytes_final - row_bytes_initial;

    } while( (row_bytes_final % 4) != 0);


    fseek(fin, bfh.offset, SEEK_SET);

    Pixel *p = new Pixel[bih.height * bih.width];

    for (uint32_t i = 0; i < (bih.height); i++) {

        for (uint32_t j = 0; j < bih.width; j++) {
            fread(&p[i], 1, sizeof(p), fin);
            i++;
            //Something I don't understand is wrong here. 
        }

        fseek(fin, padding_bytes, SEEK_CUR);

    }

    fclose(fin);


    fout = fopen(argv[2], "wb");

    if(nullptr == fout) {
        perror(argv[2]);
        return -5;
    }

    if( sizeof(BitmapFileHeader) != fwrite(
    &bfh, 
    1, 
    sizeof(bfh), 
    fout
    )) {
        printf("Unable to write bitmap file header.\n");
        return -6;
    }

    if( sizeof(BitmapInfoHeader) != fwrite(
        &bih, 
        1, 
        sizeof(bih), 
        fout
        )) {
            printf("Unable to write bitmap info header.\n");
            return -7;
        }

    fseek(fout, bfh.offset, SEEK_SET);

    for (uint32_t i = 0; i < (bih.height) ; i++) {

        for (uint32_t j = 0; i < bih.width; j++) {
            fwrite(&p[i], 1, sizeof(p), fout);
            i++;
            //same problem
        }

        fseek(fout, padding_bytes, SEEK_CUR);

    }

    fclose(fout);
    delete p;


    //fseek(fin, bfh.offset, SEEK_SET);
    //Pixel p;
    //fread(&p, 1, sizeof(p), fin);
    //printf("R = %u, G = %u, B = %u\n", p.red, p.green, p.blue);
    return 0;

}

如果我将文件指针移动到偏移量,如何覆盖输出文件的标题?

【问题讨论】:

  • 由于结构填充,您可能无法读取和写入原始结构。尝试单独阅读每个组件,以确保不会错位。
  • 这似乎是一个错字。 fwrite(&amp;p[i], 1, sizeof(p), fout); 应该是 fwrite(&amp;p[i], 1, sizeof(p[i]), fout); 它必须是 sizeof(p[i]),而不是 sizeof(p)
  • “#Pragma pack(push, 2)”和“Pragma pack(pop)”不能解决这个问题吗?
  • @tadman:为什么在读写是相同程序时填充不同?
  • 我认为这可以解决问题,但我的输出文件与输入文件大不相同;我用 for 循环进行迭代的方式可能有问题。

标签: c++ binaryfiles


【解决方案1】:

我在您的代码中看到以下问题:

问题 1

您在sizeof 中使用了错误的参数

fread(&p[i], 1, sizeof(p), fin);

fwrite(&p[i], 1, sizeof(p), fout);

他们必须是sizeof(p[i])sizeof(p[0])sizeof(Pixel)。我更喜欢sizeof(p[0]),因为无论p[0] 的类型如何,它都能正常工作。

问题 2

你有错别字

for (uint32_t j = 0; i < bih.width; j++) {

应该是

for (uint32_t j = 0; j < bih.width; j++) {
//                  ^^^ Fix

问题 3

您没有使用正确的索引来读取p 并从p 写入。

for (uint32_t i = 0; i < bih.height; i++) {
   for (uint32_t j = 0; j < bih.width; j++) {

      // Using p[i] is not correct. You need to use the right index.
      // sizeof(p[0]) is just as good as sizeof(p[index]) here.

      uint32_t index = i*bih.width + j;
      fread(&p[index], 1, sizeof(p[0]), fin);
   }
   fseek(fin, padding_bytes, SEEK_CUR);
}

写入的循环也需要同样修复。

for (uint32_t i = 0; i < bih.height; i++) {
   for (uint32_t j = 0; j < bih.width; j++) {
      uint32_t index = i*bih.width + j;
      fwrite(&p[index], 1, sizeof(p[0]), fout);
   }
   fseek(fout, padding_bytes, SEEK_CUR);
}

【讨论】:

  • 非常感谢!我可以像这样计算括号内的索引吗? fread(&p[i*bih.width +j), 1 , sizeof(p[0]), fin);
  • @PSIKLO,你可以。 IMO,在自己的语句中计算它使代码更具可读性。
  • @PSIKLO,仅供参考。您可以使您的程序相当模块化。见ideone.com/RudVCX
  • @R Sahu 谢谢。我肯定看到这写得更好。我只是在开始这个时没有考虑模块化。我一定会重写它,下次记住这一点。
【解决方案2】:

我认为 fread(&amp;p[i], 1, sizeof(p), fin); 应该是 fread(&amp;p[i], 1, sizeof(Pixel), fin);

指针的大小(基于架构)通常为 4 或 8 个字节。您的像素类是 3 个字节(使用 #pragma pack(push, 1))。

可以使用fread(&amp;p[i], sizeof(Pixel), bih.width, fin); 但请注意,排序和填充可能因不同的架构而异。

【讨论】:

  • 我进行了更改,但得到了相同的结果。尝试在 for 循环中使用 for 循环是一个坏主意吗?也许我应该给我们一个二维数组。
  • 循环也可以。一般来说,i/o 比循环慢,如果你可以在块中读取,它会更快。例如,x 次 i/o 将比单次 i/o 调用慢。
  • 谢谢!我注意到大文件的延迟。
猜你喜欢
  • 2019-04-20
  • 2019-09-18
  • 1970-01-01
  • 1970-01-01
  • 2013-07-10
  • 2020-09-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多