c从BMP获取数据

时间:2012-11-13 02:28:23

标签: c bmp

我发现自己编写了一个简单的程序来从bmp文件中提取数据。我刚刚开始,我正处于其中一个WTF时刻。

当我运行程序并提供此图像时:http://www.hack4fun.org/h4f/sites/default/files/bindump/lena.bmp

我得到了输出:

type: 19778
size: 12
res1: 0
res2: 54
offset: 2621440

实际图像大小为786,486字节。为什么我的代码报告12个字节?

指定的标题格式, http://en.wikipedia.org/wiki/BMP_file_format匹配我的BMP_FILE_HEADER结构。那为什么它会被错误的信息填满呢?

图像文件似乎没有损坏,而其他图像的输出同样错误。我错过了什么?

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

typedef struct {
    unsigned short type;
    unsigned int size;
    unsigned short res1;
    unsigned short res2;
    unsigned int offset;
} BMP_FILE_HEADER;

int main (int args, char ** argv) {
    char *file_name = argv[1];

    FILE *fp = fopen(file_name, "rb");

    BMP_FILE_HEADER file_header;

    fread(&file_header, sizeof(BMP_FILE_HEADER), 1, fp);

    if (file_header.type != 'MB') {
        printf("ERROR: not a .bmp");
        return 1;
    }

    printf("type: %i\nsize: %i\nres1: %i\nres2: %i\noffset: %i\n", file_header.type, file_header.size, file_header.res1, file_header.res2, file_header.offset);
    fclose(fp);

    return 0;
}

2 个答案:

答案 0 :(得分:2)

这里是十六进制的标题:

0000000 42 4d 36 00 0c 00 00 00 00 00 36 00 00 00 28 00
0000020 00 00 00 02 00 00 00 02 00 00 01 00 18 00 00 00

长度字段是字节36 00 0c 00`,它按英特尔顺序排列;作为32位值处理,它是0x000c0036或十进制786,486(与保存的文件大小匹配)。

可能你的C编译器正在将每个字段与32位边界对齐。启用包结构选项,pragma或指令。

答案 1 :(得分:1)

我的代码中有两个错误。

第一个错误:您必须将结构打包为1,因此每个类型的大小都与其预期的大小完全相同,因此编译器不会将其对齐,例如以4字节对齐方式。所以在你的代码short中,它不是2个字节,而是4个字节。这方面的技巧是使用编译器指令来打包最近的结构:

#pragma pack(1)

typedef struct {
    unsigned short type;
    unsigned int size;
    unsigned short res1;
    unsigned short res2;
    unsigned int offset;
} BMP_FILE_HEADER;

现在它应该正确对齐。

另一个错误就在这里:

if (file_header.type != 'MB')

您正在尝试使用short类型(使用char)检查''类型,即2个字节,即1个字节。编译器可能会给你一个警告,单引号只包含1个字符,大小为1字节,这是典型的。

要解决此问题,您可以将这2个字节分成2个1字节字符,这些字符已知(MB),并将它们组合成word。例如:

if (file_header.type != (('M' << 8) | 'B'))

如果你看到这个表达式,就会发生这种情况:

'M'(ASCII中为0x4D)向左移8位,将生成0x4D00,现在您只需添加右下角的下一个字符:0x4D00 | 0x42 = 0x4D42(其中0x42'B'的ASCII码)。像这样思考,你可以写:

if (file_header.type != 0x4D42)

然后你的代码应该可以工作。