如何将一个二进制文件转换为用C/C++数组声明其内容的文本?

93

我需要将一个二进制文件的内容包含在我的C/C++源代码中,作为一个数组声明的文本,该数组被初始化为文件的内容。 我不想在运行时动态地读取文件。 我希望执行一次操作,然后使用生成的数组声明文本。

如何将二进制文件转换为用于C/C++数组声明的文本,该数组被初始化为文件的内容?

9个回答

173

在Debian和其他Linux发行版中,默认安装了xxd工具(以及vim),该工具在使用-i选项时可以实现你想要的功能:

matteo@teodeb:~/Desktop$ echo Hello World\! > temp
matteo@teodeb:~/Desktop$ xxd -i temp 
unsigned char temp[] = {
  0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64, 0x21,
  0x0a
};
unsigned int temp_len = 13;

7
好的!它甚至可以在MacOSX上使用。 - Albert
3
在 Windows 上,我将 xxd 集成到 Visual Studio 2013 解决方案中时遇到了零问题。我使用了这个来源。 - Spike0xff
3
唯一的问题可能是,这个数组不是常量(长度也是如此)。这可能会在微控制器上产生差异(RAM vs ROM)。您可能需要编辑生成的文件。 - Tomasz Gandor
2
@TomaszGandor:是的,尽管从我在例如AVR微控制器上看到的情况来看,如果您希望它们保留在闪存中,则const将不足够,您必须添加供应商特定的属性(在gcc中为PROGMEM); 我担心这种情况对于通用工具来说可能太特定了,您可能需要编写一个特定的构建脚本。 - Matteo Italia
4
@MatteoItalia - 我同意。我已将其包装,因此将输出通过 | sed 's/unsigned/const unsigned/' 管道传递,这行代码可以包含您需要的任何其他内容。(我将结果放入一个单独的文件中 - 可以轻松地进行覆盖)。 - Tomasz Gandor
显示剩余4条评论

11

如果您在*nix-like系统上,使用xxd工具的被接受答案很好。这里是一个“一行代码”适用于任何有Python执行文件的系统:

python -c "import sys;a=sys.argv;open(a[2],'wb').write(('const unsigned char '+a[3]+'[] = {'+','.join([hex(b) for b in open(a[1],'rb').read()])+'};').encode('utf-8'))" <binary file> <header file> <array name>

<二进制文件>是您想要转换为C头文件的文件名,<头文件>是头文件的名称,<数组名称>是您想要该数组具有的名称。

上述单行Python命令大致相当于以下(可读性更强)的Python程序:

import sys

with open(sys.argv[2],'wb') as result_file:
  result_file.write(b'const char %s[] = {' % sys.argv[3].encode('utf-8'))
  for b in open(sys.argv[1], 'rb').read():
    result_file.write(b'0x%02X,' % b)
  result_file.write(b'};')

2
我使用了这个答案而不是写自己的代码。它缺少很多格式设置,而且.h文件需要一些#ifdef保护来防止多次包含,但它可以工作。赞一个。 - cmm

7

One simple tool can be found here:

#include <stdio.h>
#include <assert.h>

int main(int argc, char** argv) {
    assert(argc == 2);
    char* fn = argv[1];
    FILE* f = fopen(fn, "rb");
    printf("char a[] = {\n");
    unsigned long n = 0;
    while(!feof(f)) {
        unsigned char c;
        if(fread(&c, 1, 1, f) == 0) break;
        printf("0x%.2X,", (int)c);
        ++n;
        if(n % 10 == 0) printf("\n");
    }
    fclose(f);
    printf("};\n");
}

在 "}" 之前,您的字符数组末尾将有额外的“,”。 - rkosegi
1
这不是问题,它可以在C++中编译。 - sashoalm

2
我检查了所有可用的选项,并决定制作自己的小程序来进行转换:https://github.com/TheLivingOne/bin2array/blob/master/bin2array.c。它比bin2c和甚至是xxd都要快得多,这对于较大的文件尤其重要,特别是如果您想将转换嵌入到构建系统中。例如,在我的机器上,对于50 Mb文件:bin2c.py > 20秒,简单的Python脚本 - 约10秒,xxd - 约3秒,bin2array - 约0.4秒。此外,它产生更紧凑的输出并为数组添加对齐,以防您想在其中放置32位或64位值。

2

现在有一些应用程序或许在这个问题被提出的时候还没有推出。所以我想在这里列出我知道的当前可能的解决方案。

可执行文件和库:

  • 二进制转头文件(本地和Python命令行可执行文件)
  • 文件到C数组(GUI工具,但包括库)
  • Bin2C(仅适用于Windows,GUI界面可用,但我无法从命令行进行转换)
  • file2c(BSD和适用于某些Linux发行版)

在线工具(与问题无关,但可能对其他人有用):


0

这是一个二进制文件转C数组生成器的Python源代码,与Albert's answer中的程序完全相同。

import sys
from functools import partial

if len(sys.argv) < 2:
  sys.exit('Usage: %s file' % sys.argv[0])
print("char a[] = {")
n = 0
with open(sys.argv[1], "rb") as in_file:
  for c in iter(partial(in_file.read, 1), b''):
    print("0x%02X," % ord(c), end='')
    n += 1
    if n % 16 == 0:
      print("")
print("};")

0

这个问题虽然旧了,但是让我推荐一个简单的工具作为替代方案...

你可以使用基于GUI的工具叫做Fluid。它实际上是用于设计FLTK工具包的界面,但也可以从二进制文件生成C++的unsigned char数组。从muquit下载它。

Fluid screenshot


0

虽然这个链接可能回答了问题,但最好在这里包含答案的基本部分,并提供该链接以供参考。如果链接的页面更改,仅链接的答案可能会变得无效。- 来自审核 - user16217248

0

这个工具在C开发者命令提示符中编译。它会将内容输出到终端,显示创建的“array_name.c”文件中的内容。请注意,某些终端可能会显示“\b”字符。

    #include <stdio.h>
    #include <assert.h>

    int main(int argc, char** argv) {
    assert(argc == 2);
    char* fn = argv[1];

    // Open file passed by reference
    FILE* f = fopen(fn, "rb");
    // Opens a new file in the programs location
    FILE* fw = fopen("array_name.c","w");

    // Next two lines write the strings to the console and .c file
    printf("char array_name[] = {\n");
    fprintf(fw,"char hex_array[] = {\n");

    // Declare long integer for number of columns in the array being made
    unsigned long n = 0;

    // Loop until end of file
    while((!feof(f))){
        // Declare character that stores the bytes from hex file
        unsigned char c;

        // Ignore failed elements read
        if(fread(&c, 1, 1, f) == 0) break;
        // Prints to console and file, "0x%.2X" ensures format for all
        // read bytes is like "0x00"
        printf("0x%.2X,", (int)c);
        fprintf(fw,"0x%.2X,", (int)c);

        // Increment counter, if 20 columns have been made, begin new line
        ++n;
        if(n % 20 == 0){
            printf("\n");
            fprintf(fw,"\n");
        }
    }

    // fseek places cursor to overwrite extra "," made from previous loop
    // this is for the new .c file. Since "\b" is technically a character
    // to remove the extra "," requires overwriting it.
    fseek(fw, -1, SEEK_CUR);

    // "\b" moves cursor back one in the terminal
    printf("\b};\n");
    fprintf(fw,"};\n");
    fclose(f);
    fclose(fw);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接