Как вывести двоичный файл как строковый литерал C / C ++?

39

У меня есть двоичный файл, который я хотел бы включить в исходный код C (временно, для целей тестирования), поэтому я хотел бы получить содержимое файла в виде строки C, что-то вроде этого:

\x01\x02\x03\x04

Возможно ли это, возможно, с помощью odили hexdumpутилит? Хотя в этом нет необходимости, если строка может переноситься на следующую строку каждые 16 входных байтов и включать двойные кавычки в начале и конце каждой строки, это было бы еще лучше!

Я знаю, что в строку будет встроен nulls ( \x00), поэтому мне нужно будет указать длину строки в коде, чтобы эти байты не заканчивали строку раньше.

Malvineous
источник
stackoverflow.com/q/13856930/560648
Легкость гонок с Моникой
Я хочу похожий, но сохраняющий ascii печатный глиф, только экранирующий 1-127, цитату, обратную косую черту,
ноль

Ответы:

10

Вы можете почти делать то, что хотите 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 в комментариях, мы также должны передать параметр -vverbose, hexdumpчтобы он не сокращал длинные серии идентичных байтов *.

hexdump -v -e '16/1 "_x%02X" "\n"' filename | sed 's/_/\\/g; s/\\x  //g; s/.*/    "&"/'
PM 2Ring
источник
Это создает избыточные и недействительные элементы, если ввод короче 16 байтов.
Cengiz Can
@CengizCan:: упс! Это лучше?
PM 2Ring 27.12.14
1
Нужно добавить -vопцию в hexdump, иначе длинные пробеги одного и того же входного байта приводят к выводу строк, которые говорят "*".
Malvineous
@ Зловещий Хороший вопрос! Я исправил свой ответ. Спасибо за хедз-ап (и спасибо, что приняли мой ответ).
PM 2Ring
66

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исходного кода.

Майкл Гомер
источник
Ницца! Я даже не знал, что у меня был XXX. Теперь я просто должен помнить, что он существует в следующий раз, когда он мне понадобится ... или я, вероятно, просто скопирую необходимые функции в Python. :)
PM 2Ring
objcopyбыло бы лучше
Легкость Гонки с Моникой
@LightnessRacesinOrbit objcopyпозволит OP связывать двоичные данные с исполняемым файлом в виде объектного файла, что полезно, но не совсем то, о чем здесь идет речь .
Странствуй Наута
1
@WanderNauta: Вы бы получили к нему доступ почти так же, как и к этому foo/ foo_lenздесь, и вы бы не тратили много места на хранение. Я убежден, что ОП будет лучше objcopyи что он отвечает его или ее требованиям.
Легкость гонок с Моникой
2
objcopyхорошо, когда он рядом, но он не портативный, а выход еще меньше. Это, безусловно, может быть частью хорошего постоянного решения, но здесь вопрос не в этом.
Майкл Гомер
3

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

Это не строковый литерал, но по сути это то же самое, что и строковый литерал во время компиляции (учтите, что строковые литералы на самом деле не существуют во время выполнения; на самом деле, ни один из других ответов на самом деле не дает вам строковый литерал даже во время компиляции) и к ним можно получить доступ практически таким же образом:

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

Недостатком является то, что вам нужно указать целевую архитектуру, чтобы сделать объектный файл совместимым, и это может быть не тривиально в вашей системе сборки.

Гонки легкости с Моникой
источник
2

Должно быть именно то, что вы просили:

hexdump -v -e '"\\" "x" 1/1 "%02X"' file.bin ; echo
Schtrudel
источник
0

Это короткая утилита, которую я написал, которая, по сути, делает то же самое (изначально размещена в 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;
}
дубильщик
источник
1
Ваш ответ будет более полезным, если вы также предоставите входные и выходные примеры с ним.
not2qubit
0

Если вы в Python, загрузите его в переменную «buff» и используйте что-то вроде этого:

buff2 = buff.encode("hex")
print ("0x"+", 0x".join([buff2[i:i+2] for i in range(0,len(buff2),2)]))
TimSC
источник