91

I need to include the contents of a binary file in my C/C++ source code as the text for the declaration of an array initialized to the content of the file. I'm not looking to read the file dynamically at runtime. I want to perform the operation once and then use the generated array declaration text.

How can I convert a binary file to the text for the C/C++ declaration of an array which is initialized to the contents of the file?

Makyen
  • 31,849
  • 12
  • 86
  • 121
Albert
  • 65,406
  • 61
  • 242
  • 386

9 Answers9

169

On Debian and other Linux distros is installed by default (along with vim) the xxd tool, which, given the -i option, can do what you want:

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;
Matteo Italia
  • 123,740
  • 17
  • 206
  • 299
  • 7
    Ah nice! It's even available in MacOSX. – Albert Jan 03 '12 at 05:02
  • 3
    had zero trouble integrating xxd into a Visual Studio 2013 solution on Windows. I used [this source](http://www.opensource.apple.com/source/vim/vim-43/vim/src/xxd/xxd.c?txt) – Spike0xff Dec 03 '15 at 15:23
  • 2
    The only problem could be, that this array is not const (as well as the length). This may make a difference on microcontrollers (RAM vs ROM). You may need to edit the generated files. – Tomasz Gandor Jun 21 '16 at 13:32
  • 2
    @TomaszGandor: yep, although from what I saw on e.g. AVR microcontrollers `const` would not be enough, if you want them to stay in flash memory you have to add vendor-specific attributes (`PROGMEM` in gcc); I fear that such a case is probably too specific for a general purpose tool, you probably will want to write a specific build script. – Matteo Italia Jun 21 '16 at 16:33
  • 4
    @MatteoItalia - I agree. I have it wrapped, so that is pipes the output through `| sed 's/unsigned/const unsigned/'`, and it could say anything else you need in this line. (I put the result into a separate file - to be easily overwritten). – Tomasz Gandor Jun 22 '16 at 11:51
  • Note that the size of the included header will be larger than the original binary file , this might not be a problem but in some cases g++ won't compile the source outputting the error : "cc1plus.exe: out of memory allocating *********** bytes" – mahmoud nezar sarhan Jul 19 '17 at 18:08
  • And what's about 16 or 32-bit data? – Eddy_Em May 29 '18 at 20:39
  • Excellent! The fact that xxd is available by default makes it very nice to integrate in a build step (be it a Make target, a CMake target, etc). – Stéphane Gourichon Nov 14 '19 at 21:41
  • It's also not static, which is a problem when you don't want to dirty your namespace. – Remember Monica Mar 07 '21 at 09:17
11

The accepted answer using xxd tool is nice if you are on a *nix-like system. Here is a "one-liner" for any system that has python executable on the path:

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>

< binary file > is the name of the file you want to turn into a C header, < header file > is the name of the header file, and < array name > is the name you want the array to have.

The above one-line Python command does approximately the same as the following (much more readable) Python program:

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'};')
astraujums
  • 709
  • 8
  • 20
  • 2
    I used this answer rather than writing my own. It is missing a lot of formatting, and the .h file could use some #ifdef protection against multiple inclusion, but it worked. +1 for working. – cmm Apr 12 '20 at 21:37
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");
}
sashoalm
  • 75,001
  • 122
  • 434
  • 781
Albert
  • 65,406
  • 61
  • 242
  • 386
2

I checked all available options and decided to make my own little program to do the conversion:

https://github.com/TheLivingOne/bin2array/blob/master/bin2array.c

It works much faster than bin2c and even xxd which is important for larger files, especially if you want to embed the conversion into your build system. E.g. for 50 Mb file on my machine:

bin2c.py > 20 sec

Simple Python scripts - about 10 sec

xxd - about 3 sec

bin2array - about 0.4 sec

Also, it produces much more compact output and adds alignment to the array, in case you want to put 32 or 64 bit values there.

Sergey Galin
  • 426
  • 6
  • 8
2

There are a few apps now that perhaps at the time this question was posed were not available. So I would like to list the ones that I know of here as current potential solutions.

Executables & libraries:

  • Binary to Header (Native & Python command line executables)
  • File To C Array (GUI tool but does include a library)
  • Bin2C (Windows only, GUI interface works but I couldn't convert from command line)
  • file2c (BSD & ported to some Linux distros)

Online tools (not relevant to question but may be useful to others):

AntumDeluge
  • 490
  • 1
  • 5
  • 13
0

This tool compiles in the developer command prompt in C. It produces output to the terminal displaying the contents in the "array_name.c" file that is created. Note that some terminals may display the "\b" character.

    #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);
}
m_h
  • 1
  • 1
0

This is a binary file to C array generator python source code which is identical program in 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("};")
Hill
  • 3,391
  • 3
  • 24
  • 28
0

The question is old but let me suggest simple tool which can be used as alternative...

You can use GUI based tool called Fluid. It is actually used for designing interface for FLTK toolkit but can also generate unsigned char array for C++ from binary file. Download it from muquit.

Fluid screenshot

WailenB
  • 93
  • 7
0

You can use ld. Refer https://github.com/termux/termux-packages/issues/16429#issuecomment-1541466535. I reorganize it in an article.

wzy
  • 1
  • While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. - [From Review](/review/late-answers/34367252) – user16217248 May 15 '23 at 23:18