Làm cách nào để kết xuất tệp nhị phân dưới dạng chuỗi ký tự C / C ++?


39

Tôi có một tệp nhị phân mà tôi muốn đưa vào mã nguồn C của mình (tạm thời, cho mục đích thử nghiệm) vì vậy tôi muốn lấy nội dung tệp dưới dạng chuỗi C, đại loại như sau:

\x01\x02\x03\x04

Điều này có thể, có lẽ bằng cách sử dụng odhoặc hexdumpcác tiện ích? Mặc dù không cần thiết, nếu chuỗi có thể quấn đến dòng tiếp theo cứ sau 16 byte đầu vào và bao gồm dấu ngoặc kép ở đầu và cuối của mỗi dòng, điều đó sẽ còn đẹp hơn nữa!

Tôi biết rằng chuỗi sẽ nhúng null ( \x00) vì vậy tôi sẽ cần chỉ định độ dài của chuỗi trong mã, để ngăn các byte này kết thúc chuỗi sớm.



Tôi muốn tương tự nhưng giữ lại glyph có thể in ascii, chỉ thoát 1-127, trích dẫn, dấu gạch chéo ngược, null, v.v.
友情 留 在 无

Câu trả lời:


10

Bạn gần như có thể làm những gì bạn muốn hexdump, nhưng tôi không thể tìm ra cách lấy dấu ngoặc kép & dấu gạch chéo đơn vào chuỗi định dạng. Vì vậy, tôi làm một chút xử lý hậu kỳ với sed. Như một phần thưởng, tôi cũng đã thụt lề mỗi dòng bằng 4 khoảng trắng. :)

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

Chỉnh sửa

Như Cengiz Can đã chỉ ra, dòng lệnh trên không đối phó tốt với các dòng dữ liệu ngắn. Vì vậy, đây là một phiên bản cải tiến mới:

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

Như Malvineous đề cập trong các bình luận, chúng ta cũng cần chuyển -vtùy chọn dài dòng hexdumpđể ngăn nó viết tắt các chuỗi dài của các byte giống hệt nhau *.

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

Điều này tạo ra các phần tử dự phòng và không hợp lệ nếu đầu vào ngắn hơn 16 byte.
Cengiz Can

@CengizCan :: ôi :! Điều đó có tốt hơn không?
PM 2Ring

1
Cần thêm -vtùy chọn hexdump, nếu không, việc chạy dài của cùng một byte đầu vào gây ra các dòng đầu ra "*".
Malvineous

@Malvineous Điểm tốt! Tôi đã sửa đổi câu trả lời của mình. Cảm ơn vì đã ngẩng cao đầu (và cảm ơn vì đã chấp nhận câu trả lời của tôi).
PM 2Ring

66

xxdcó một chế độ cho việc này. Các -i/ --includetùy chọn sẽ:

đầu ra trong C bao gồm kiểu tệp. Một định nghĩa mảng tĩnh hoàn chỉnh được viết (được đặt tên theo tệp đầu vào), trừ khi xxd đọc từ stdin.

Bạn có thể kết xuất nó vào một tệp thành #included, và sau đó chỉ cần truy cập foonhư bất kỳ mảng ký tự nào khác (hoặc liên kết nó trong). Nó cũng bao gồm một tuyên bố về độ dài của mảng.

Đầu ra được gói đến 80 byte và trông giống như những gì bạn có thể viết bằng tay:

$ 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;

xxdlà, một phần kỳ lạ, một phần của vimphân phối, vì vậy bạn có thể đã có nó. Nếu không, đó là nơi bạn có được nó - bạn cũng có thể tự mình xây dựng công cụ này từ vimnguồn.


Tốt đẹp! Tôi thậm chí không biết tôi đã có xxd. Bây giờ tôi chỉ cần nhớ nó tồn tại vào lần tới khi tôi cần nó ... hoặc có lẽ tôi sẽ chỉ sao chép chức năng cần thiết trong Python. :)
PM 2Ring

objcopysẽ tốt hơn
Cuộc đua nhẹ nhàng với Monica

@LightnessRacesinOrbit objcopysẽ cho phép OP liên kết dữ liệu nhị phân với tệp thực thi dưới dạng tệp đối tượng, rất hữu ích nhưng không chính xác những gì đang được hỏi ở đây.
Đi lang thang Nauta

1
@WanderNauta: Bạn sẽ truy cập nó theo cách tương tự như bạn truy cập foo/ foo_lenở đây, bạn sẽ không lãng phí rất nhiều không gian lưu trữ. Tôi tin rằng OP sẽ tốt hơn objcopyvà phù hợp với yêu cầu của anh ấy hoặc cô ấy.
Cuộc đua nhẹ nhàng với Monica

2
objcopyvẫn ổn khi nó ở xung quanh, nhưng nó không di động và đầu ra thậm chí còn ít hơn thế. Nó chắc chắn có thể là một phần của một giải pháp lâu dài tốt, nhưng đó không phải là câu hỏi ở đây.
Michael Homer

3

xxd là tốt nhưng kết quả rất dài dòng và tốn nhiều dung lượng lưu trữ.

Bạn có thể đạt được thực tế điều tương tự bằng cách sử dụng objcopy; ví dụ

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

Sau đó liên kết foo.ođến chương trình của bạn và chỉ cần sử dụng các biểu tượng sau:

00000550 D _binary_foo_end
00000550 A _binary_foo_size 
00000000 D _binary_foo_start

Đây không phải là một chuỗi ký tự, nhưng về cơ bản nó giống như những gì một chuỗi ký tự biến thành trong quá trình biên dịch (xem xét rằng các chuỗi ký tự chuỗi trên thực tế không tồn tại vào thời gian chạy; thực sự, không có câu trả lời nào khác thực sự cung cấp cho bạn một chuỗi ký tự ngay cả tại thời gian biên dịch) và có thể được truy cập theo cách tương tự:

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

Nhược điểm là bạn cần chỉ định kiến ​​trúc đích của mình để làm cho tệp đối tượng tương thích và điều này có thể không tầm thường trong hệ thống xây dựng của bạn.


2

Nên chính xác những gì bạn yêu cầu:

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

0

Đây là một tiện ích ngắn mà tôi đã viết về cơ bản thực hiện điều tương tự (ban đầu được đăng trên 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
Câu trả lời của bạn sẽ hữu ích hơn nếu bạn cũng cung cấp các ví dụ đầu vào và đầu ra với nó.
not2qubit 7/03/2015

0

Nếu bạn vào python, tải nó vào một "buff" biến và sử dụng một cái gì đó như thế này:

buff2 = buff.encode("hex")
print ("0x"+", 0x".join([buff2[i:i+2] for i in range(0,len(buff2),2)]))
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.