2

私は自分がbmpファイルからデータを抽出するための簡単なプログラムを書いていることに気づきました。私は始めたばかりで、それらのWTFの瞬間の1つにいます。

プログラムを実行してこの画像を提供すると、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;
}
4

2 に答える 2

2

16 進数のヘッダーは次のとおりです。

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 または 10 進数の 786,486 (保存されたファイル サイズに一致) です。

おそらく、C コンパイラは各フィールドを 32 ビット境界に揃えています。パック構造オプション、プラグマ、またはディレクティブを有効にします。

于 2012-11-13T02:43:08.143 に答える
1

あなたのコードには 2 つの間違いがあります。

最初の間違い: 構造体を 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')

short2 バイトのcharタイプを ( を使用して) 1 バイトのタイプでチェックしようとして''います。おそらく、コンパイラはそれについて警告を発しています。単一引用符に 1 バイト サイズの文字が 1 つしか含まれていないことは標準的です。

これを回避するには、この 2 バイトを 2 つの 1 バイト文字 (MおよびB) に分割し、それらをword. 例えば:

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

この表現を見ると、次のようになります。

'M'(これは0x4DASCII です) 左に 8 ビット シフトすると、結果は0x4D00になります。これで、またはまたは次の文字を右のゼロに追加できます: (ここではASCII です)。このように考えると、次のように書くことができます。0x4D00 | 0x42 = 0x4D420x42'B'

if (file_header.type != 0x4D42)

その後、コードが機能するはずです。

于 2012-11-13T02:48:26.450 に答える