web-dev-qa-db-ja.com

バイナリファイルをC / C ++文字列リテラルとしてダンプする方法は?

(一時的に、テストの目的で)Cソースコードに含めたいバイナリファイルがあるので、ファイルの内容を次のようなC文字列として取得します。

\x01\x02\x03\x04

これは、おそらくodまたはhexdumpユーティリティを使用することによって可能ですか?必須ではありませんが、文字列が16入力バイトごとに次の行に折り返され、各行の先頭と末尾に二重引用符が含まれている場合は、さらに便利です。

文字列に埋め込まれたnull(\x00)したがって、これらのバイトが文字列を早期に終了しないように、コードで文字列の長さを指定する必要があります。

41
Malvineous

あなたはほとんどhexdumpであなたが望むことをすることができます、しかし私は引用符と単一のバックスラッシュをフォーマットに取得する方法を理解できませんストリング。ですから、sedを使用して少し後処理を行います。おまけとして、各行を4つのスペースでインデントしました。 :)

hexdump -e '16/1 "_x%02X" "\n"' filename | sed 's/_/\\/g; s/.*/    "&"/'

編集

Cengiz Canが指摘したように、上記のコマンドラインは短いデータラインにうまく対応していません。だからここに新しい改良版があります:

hexdump -e '16/1 "_x%02X" "\n"' filename | sed 's/_/\\/g; s/\\x  //g; s/.*/    "&"/'

Malvineousがコメントで言及しているように、-v verboseオプションをhexdumpに渡して、同じバイトの長い実行が*に短縮されないようにする必要もあります。

hexdump -v -e '16/1 "_x%02X" "\n"' filename | sed 's/_/\\/g; s/\\x  //g; s/.*/    "&"/'
11
PM 2Ring

xxd にはこのためのモードがあります。 -i/--includeオプションは:

cインクルードファイルスタイルでの出力。 xxdがstdinから読み取らない限り、完全な静的配列定義が書き込まれます(入力ファイルにちなんで名前が付けられます)。

これをファイルにダンプして#included、そして他の文字配列と同じようにfooにアクセス(またはリンク)します。また、配列の長さの宣言も含まれます。

出力は80バイトに折り返され、基本的に手動で記述したものに似ています。

$ xxd --include foo
unsigned char foo[] = {
  0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64,
  0x21, 0x0a, 0x0a, 0x59, 0x6f, 0x75, 0x27, 0x72, 0x65, 0x20, 0x76, 0x65,
  0x72, 0x79, 0x20, 0x63, 0x75, 0x72, 0x69, 0x6f, 0x75, 0x73, 0x21, 0x20,
  0x57, 0x65, 0x6c, 0x6c, 0x20, 0x64, 0x6f, 0x6e, 0x65, 0x2e, 0x0a
};
unsigned int foo_len = 47;

xxdは、少し奇妙なことに、vimディストリビューションの一部であるため、すでに持っている可能性があります。そうでない場合は、ここで入手できます。vimソースから独自にツールを構築することもできます。

72
Michael Homer

xxdは適切ですが、結果は非常に冗長になり、多くのストレージスペースを必要とします。

objcopy を使用して、実質的に同じことを達成できます。例えば.

objcopy --input binary \
    --output elf32-i386 \
    --binary-architecture i386 foo foo.o

次に、foo.oをプログラムにリンクし、次の記号を使用します。

00000550 D _binary_foo_end
00000550 A _binary_foo_size 
00000000 D _binary_foo_start

これは文字列リテラルではありませんが、コンパイル時に文字列リテラルが変わるのと基本的に同じです(実際には文字列literalsが存在しないことを考慮してください)実行時;実際、他の回答のいずれも実際にはコンパイル時でも文字列リテラルを提供せず)、ほとんど同じ方法でアクセスできます。

unsigned char* ptr = _binary_foo_start;
int i;
for (i = 0; i < _binary_foo_size; i++, ptr++)
   putc(*ptr);

欠点は、オブジェクトファイルに互換性を持たせるためにターゲットアーキテクチャを指定する必要があることです。これは、ビルドシステムでは簡単ではない場合があります。

あなたが正確にあなたが求めたものでなければなりません:

hexdump -v -e '"\\" "x" 1/1 "%02X"' file.bin ; echo
2
Schtrudel

これは私が書いた短いユーティリティであり、本質的に同じことを行います(元は Stack Overflow に投稿されていました)。

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

#define MAX_LENGTH 80

int main(void)
{
    FILE *fout = fopen("out.txt", "w");

    if(ferror(fout))
    {
        fprintf(stderr, "Error opening output file");
        return 1;
    }
    char init_line[]  = {"char hex_array[] = { "};
    const int offset_length = strlen(init_line);

    char offset_spc[offset_length];

    unsigned char buff[1024];
    char curr_out[64];

    int count, i;
    int line_length = 0;

    memset((void*)offset_spc, (char)32, sizeof(char) * offset_length - 1);
    offset_spc[offset_length - 1] = '\0';

    fprintf(fout, "%s", init_line);

    while(!feof(stdin))
    {
        count = fread(buff, sizeof(char), sizeof(buff) / sizeof(char), stdin);

        for(i = 0; i < count; i++)
        {
            line_length += sprintf(curr_out, "%#x, ", buff[i]);

            fprintf(fout, "%s", curr_out);
            if(line_length >= MAX_LENGTH - offset_length)
            {
                fprintf(fout, "\n%s", offset_spc);
                line_length = 0;
            }
        }
    }
    fseek(fout, -2, SEEK_CUR);
    fprintf(fout, " };");

    fclose(fout);

    return EXIT_SUCCESS;
}
0
Tanner

Pythonを使用している場合は、それを変数「buff」にロードして、次のようなものを使用します。

buff2 = buff.encode("hex")
print ("0x"+", 0x".join([buff2[i:i+2] for i in range(0,len(buff2),2)]))
0
TimSC