¿Cómo puedo convertir un archivo binario al texto que declara una matriz C/C++ con ese contenido?

6 minutos de lectura

avatar de usuario
Alberto

Necesito incluir el contenido de un archivo binario en mi código fuente C/C++ como texto para la declaración de una matriz inicializada en el contenido del archivo. No estoy buscando leer el archivo dinámicamente en tiempo de ejecución. Quiero realizar la operación una vez y luego usar el texto de declaración de matriz generado.

¿Cómo puedo convertir un archivo binario al texto de la declaración C/C++ de una matriz que se inicializa en el contenido del archivo?

En Debian y otras distribuciones de Linux se instala de forma predeterminada (junto con vim) la xxd herramienta que, dada la -i opción, puede hacer lo que quiera:

[email protected]:~/Desktop$ echo Hello World\! > temp
[email protected]:~/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;

  • ¡Oh bien! Incluso está disponible en MacOSX.

    – Alberto

    3 de enero de 2012 a las 5:02

  • no tuvo problemas para integrar xxd en una solución de Visual Studio 2013 en Windows. solía esta fuente

    – Spike0xff

    3 de diciembre de 2015 a las 15:23


  • El único problema podría ser que esta matriz no sea constante (así como la longitud). Esto puede marcar la diferencia en los microcontroladores (RAM vs ROM). Es posible que deba editar los archivos generados.

    – Tomasz Gandor

    21 de junio de 2016 a las 13:32

  • @TomaszGandor: sí, aunque por lo que vi en, por ejemplo, microcontroladores AVR const no sería suficiente, si desea que permanezcan en la memoria flash, debe agregar atributos específicos del proveedor (PROGMEM en CCG); Me temo que tal caso es probablemente demasiado específico para una herramienta de propósito general, probablemente querrá escribir un script de compilación específico.

    – Mateo Italia

    21 de junio de 2016 a las 16:33


  • @MatteoItalia – Estoy de acuerdo. Lo tengo envuelto, por lo que canaliza la salida a través de | sed 's/unsigned/const unsigned/', y podría decir cualquier otra cosa que necesite en esta línea. (Puse el resultado en un archivo separado, para que se sobrescriba fácilmente).

    – Tomasz Gandor

    22 de junio de 2016 a las 11:51

avatar de usuario
astraujums

La respuesta aceptada usando la herramienta xxd es buena si está en un sistema tipo * nix. Aquí hay una “línea única” para cualquier sistema que tenga un ejecutable de python en la ruta:

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>

es el nombre del archivo que desea convertir en un encabezado de C, es el nombre del archivo de encabezado y es el nombre que desea que tenga la matriz.

El comando Python de una línea anterior hace aproximadamente lo mismo que el siguiente programa Python (mucho más legible):

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'};')

  • Usé esta respuesta en lugar de escribir la mía. Le falta mucho formato, y el archivo .h podría usar alguna protección #ifdef contra la inclusión múltiple, pero funcionó. +1 por trabajar.

    – cmm

    12 abr 2020 a las 21:37

avatar de usuario
Alberto

Una herramienta simple se puede encontrar aquí:

#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");
}

  • Tendrá “,” adicional al final de su matriz de caracteres antes de “}”

    – rkosegi

    04/01/2013 a las 14:30

  • Eso no es un problema, se compila en C++.

    – sashoalm

    28 de agosto de 2013 a las 8:08

avatar de usuario
sergey galin

Revisé todas las opciones disponibles y decidí hacer mi propio pequeño programa para hacer la conversión:

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

Funciona mucho más rápido que bin2c e incluso xxd, lo cual es importante para archivos más grandes, especialmente si desea integrar la conversión en su sistema de compilación. Por ejemplo, para un archivo de 50 Mb en mi máquina:

bin2c.py > 20 segundos

Scripts simples de Python: aproximadamente 10 segundos

xxd – alrededor de 3 segundos

bin2array – alrededor de 0,4 segundos

Además, produce una salida mucho más compacta y agrega alineación a la matriz, en caso de que desee colocar valores de 32 o 64 bits allí.

Esta herramienta se compila en el símbolo del sistema del desarrollador en C. Produce una salida en el terminal que muestra el contenido del archivo “array_name.c” que se crea. Tenga en cuenta que algunos terminales pueden mostrar el carácter “\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);
}

Este es un archivo binario para el código fuente de Python del generador de matrices C, que es un programa idéntico en la respuesta de Albert.

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("};")

avatar de usuario
WailenB

La pregunta es antigua, pero permítanme sugerir una herramienta simple que se puede usar como alternativa …

Puede usar una herramienta basada en GUI llamada Fluid. En realidad, se usa para diseñar la interfaz para el kit de herramientas FLTK, pero también puede generar una matriz de caracteres sin firmar para C ++ a partir de un archivo binario. Descárgalo de muquit.

Captura de pantalla fluida

¿Ha sido útil esta solución?