【问题标题】:fread into struct reads data incorrectlyfread into struct 读取数据不正确
【发布时间】:2016-06-12 21:35:25
【问题描述】:

我正在尝试将位图 (.bmp) 图像标题读入 c 中的 struct

typedef unsigned short WORD;
typedef unsigned long DWORD;

typedef struct _BITMAPFILEHEADER {
    WORD Type;
    DWORD Size;
    WORD Reserved1;
    WORD Reserved2;
    DWORD OffBits;
} BITMAPFILEHEADER;

我读取位图文件的代码

FILE *fp;
BITMAPFILEHEADER header;

fp = fopen(file,"rb");
if (fp == NULL) {
    printf("cannot open file!\n");
    return 1;
}

fread(&header, sizeof(BITMAPFILEHEADER), 1, fp);

printf("Type: %02x\n", header.Type);
printf("Size: %04x\n", header.Size);
printf("Reserved: %02x\n", header.Reserved1);
printf("Reserved: %02x\n", header.Reserved2);
printf("Offset: %04x\n", header.OffBits);

一切都应该相等:
类型:0x424d
尺寸:0x00060436
保留1:0x00
保留2:0x00
偏移量:0x00000436

实际发生了什么(printf 输出):
类型:0x424d
尺寸:0x0006
保留:0x002
保留:0x436
偏移量:0x280000

(如果有帮助,我的操作系统是 32 位 ubuntu)

【问题讨论】:

  • 如果您需要固定宽度的整数,请使用标准提供的整数。不要使用自制类型。另请阅读 abolut struct 布局以及如何正确(反)序列化/编组八位字节流。
  • 数据是如何写入文件的?您需要模仿读取数据时的写入方式。如果结构有填充(几乎可以肯定)但数据是零散写入的(好像没有填充——几乎可以肯定是这样),那么数据就不能像你那样被读取。您需要在单独的 fread() 操作中依次读取每个元素 - 在使用您认为返回的内容之前,您应该检查每个 fread() 是否成功。
  • 这将有助于显示文件的内容。我从您的输出中猜测该文件实际上是从 4D 42 36 04 06 00 00 00
  • 标准位图头文件需要“打包”。

标签: c struct fread


【解决方案1】:

在读取此类文件时,我认为一个好的方法是首先将它们作为字节流读取 (unsigned char),然后在需要时根据正确的数据类型解释部分文件内容.

例如,在您的情况下,我会像这样定义 BITMAPFILEHEADER 结构:

typedef struct _BITMAPFILEHEADER {
    unsigned char   Type[ 2 ];
    unsigned int    Size;          // Size of the BMP file in bytes 
    unsigned char   Reserved1[ 2 ];
    unsigned char   Reserved2[ 2 ];
    unsigned int    OffBits;       // Starting address of the pixel array 
} BITMAPFILEHEADER;

然后,我会将标头内容读取为 14 字节的块,最后,我将继续正确填充 BITMAPFILEHEADER 结构。

您可以在下面找到一个简单的程序,它读取 BMP 文件的标题并将其打印到屏幕上。

int main
    (
    )
    {
    // Defines a few variables
    FILE*           fp   =  0;
    unsigned char   h[ 14 ];

    // Opens the BMP file
    fp   =   fopen( "img.bmp", "rb" );

    if ( fp == NULL )
        {
        printf( "Cannot open file\n" );
        return -1;
        }

    // Reads the BMP header (14 bytes)
    fread( h, 14, 1, fp );

    // Prints the header contents
    printf( "Type:     %02x%02x\n", h[ 0 ], h[ 1 ] );
    printf( "Size:     %02x%02x%02x%02x\n", h[ 2 ], h[ 3 ], h[ 4 ], h[ 5 ] );
    printf( "Reserved: %02x%02x\n", h[ 6 ], h[ 7 ] );
    printf( "Reserved: %02x%02x\n", h[ 8 ], h[ 9 ] );
    printf( "Offset:   %02x%02x%02x%02x\n", h[ 10 ], h[ 11 ], h[ 12 ], h[ 13 ] );

    return 0;
    }

注意 1 - 填充:根据 BMP 文件格式规范,我们知道标题是 14 字节长,而 printf( "%d", sizeof(BITMAPFILEHEADER) ) 将显示不同的(更大的!)数字,因为填充 (请参阅您的问题的 cmets)。

注意 2 - 字节序:分别将 2 或 4 个字节写入短字节或长字节时,您必须考虑字节序。这意味着您必须知道值是如何写入文件的(对于位图标题,它们使用小端符号表示)以及您的机器如何处理它们(可能是小端符号)。

【讨论】:

  • 假设您的意思是unsigned char Type[2];,您的结构定义并没有真正帮助,因为在Size 之前仍然会有填充。最好像在示例程序中那样读取预期大小的无符号字符缓冲区。但是您的程序存在字节顺序问题。
  • BMP 文件总是在 little-endian afaik 上。
  • @M.M 当然我的意思是unsigned char Type[2];。只是一个疏忽——正如你所看到的,我在main 中定义h 数组时使用了正确的语法。我相应地编辑了我的答案;非常感谢您指出!
  • @M.M 正如您正确注意到的那样,我从来没有打算将我的结构用作fread 的目标,因此填充不是问题。不知道 OP 代码的用途,我的结构旨在尽可能与原始结构相似,同时表明将字节 [2-5] 和字节 [10-13] 解释为无符号整数是一个合理的选择 - 因为它们分别是大小和偏移量 - 而其余部分则保留为原始字节序列。至于字节序,我清楚地将 BMP 标头字段逐个字段打印为原始字节序列——就像 OP 似乎做的那样——但后来我添加了 NOTE 2。
  • @rici 你是对的 (Bitmap file header)。我刚刚在我的答案中添加了更多细节。
猜你喜欢
  • 2016-08-24
  • 2021-02-16
  • 1970-01-01
  • 2023-03-25
  • 1970-01-01
  • 1970-01-01
  • 2013-03-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多