"#include" текстовый файл в программе на C как символ [] - PullRequest
117 голосов
/ 04 января 2009

Есть ли способ включить весь текстовый файл в виде строки в программу на C во время компиляции?

что-то вроде:

  • file.txt:

    This is
    a little
    text file
    
  • main.c:

    #include <stdio.h>
    int main(void) {
       #blackmagicinclude("file.txt", content)
       /*
       equiv: char[] content = "This is\na little\ntext file";
       */
       printf("%s", content);
    }
    

получение маленькой программы, которая печатает на стандартный вывод "Это немного текстовый файл "

В данный момент я использовал хакерский скрипт на python, но он довольно уродлив и ограничен только одним именем переменной, можете ли вы сказать мне другой способ сделать это?

Ответы [ 16 ]

122 голосов
/ 04 января 2009

Я бы предложил использовать (unix util) xxd для этого. Вы можете использовать это так

$ echo hello world > a
$ xxd -i a

выходы:

unsigned char a[] = {
  0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x0a
};
unsigned int a_len = 12;
88 голосов
/ 29 июля 2014

Вопрос был о C, но в случае, если кто-то попытается сделать это с C ++ 11, тогда это можно сделать с небольшими изменениями во включенном текстовом файле благодаря новым необработанным строковым литералам :

В C ++ сделайте это:

const char *s =
#include "test.txt"
;

В текстовом файле сделать это:

R"(Line 1
Line 2
Line 3
Line 4
Line 5
Line 6)"

Таким образом, должен быть только префикс в верхней части файла и суффикс в конце. Между ними вы можете делать то, что хотите, особого экранирования не требуется, если вам не нужна последовательность символов )". Но даже это может сработать, если вы укажете свой собственный разделитель:

R"=====(Line 1
Line 2
Line 3
Now you can use "( and )" in the text file, too.
Line 5
Line 6)====="
14 голосов
/ 04 января 2009

У вас есть две возможности:

  1. Используйте расширения компилятора / компоновщика для преобразования файла в двоичный файл с соответствующими символами, указывающими на начало и конец двоичных данных. Смотрите этот ответ: Включите двоичный файл со скриптом GNU ld linker .
  2. Преобразуйте ваш файл в последовательность символьных констант, которые могут инициализировать массив. Обратите внимание, что вы не можете просто сделать "" и охватить несколько строк. Вам понадобится символ продолжения строки (\), экранирующие символы " и другие, чтобы это работало. Проще написать небольшую программу для преобразования байтов в последовательность типа '\xFF', '\xAB', ...., '\0' (или использовать инструмент unix xxd, описанный в другом ответе, если он у вас есть!):

Код:

#include <stdio.h>

int main() {
    int c;
    while((c = fgetc(stdin)) != EOF) {
        printf("'\\x%X',", (unsigned)c);
    }
    printf("'\\0'"); // put terminating zero
}

(не тестировалось). Тогда сделайте:

char my_file[] = {
#include "data.h"
};

Где data.h генерируется

cat file.bin | ./bin2c > data.h
8 голосов
/ 05 января 2009

хорошо, вдохновленный публикацией Daemin's , которую я протестировал на следующем простом примере:

a.data:

"this is test\n file\n"

test.c:

int main(void)
{
    char *test = 
#include "a.data"
    ;
    return 0;
}

gcc -E выход test.c:

# 1 "test.c"
# 1 "<built-in>"
# 1 "<command line>"
# 1 "test.c"

int main(void)
{
    char *test =
# 1 "a.data" 1
"this is test\n file\n"
# 6 "test.c" 2
    ;
    return 0;
}

Так что он работает, но требует данных, заключенных в кавычки.

4 голосов
/ 13 декабря 2017

Мне нравится ответ каяра. Однако, если вы не хотите прикасаться к входным файлам , и если вы используете CMake , вы можете добавить в файл последовательности символов разделителя. Например, следующий код CMake копирует входные файлы и соответствующим образом переносит их содержимое:

function(make_includable input_file output_file)
    file(READ ${input_file} content)
    set(delim "for_c++_include")
    set(content "R\"${delim}(\n${content})${delim}\"")
    file(WRITE ${output_file} "${content}")
endfunction(make_includable)

# Use like
make_includable(external/shaders/cool.frag generated/cool.frag)

Затем включите в C ++, как это:

constexpr char *test =
#include "generated/cool.frag"
;
3 голосов
/ 04 января 2009

Что может сработать, если вы сделаете что-то вроде:

int main()
{
    const char* text = "
#include "file.txt"
";
    printf("%s", text);
    return 0;
}

Конечно, вы должны быть осторожны с тем, что на самом деле находится в файле, проверяя, нет ли двойных кавычек, экранированы ли все соответствующие символы и т. Д.

Поэтому может быть проще, если вы просто загрузите текст из файла во время выполнения или вставите текст прямо в код.

Если вы все еще хотите получить текст в другом файле, вы можете поместить его там, но он должен быть представлен в виде строки. Вы должны использовать код, как указано выше, но без двойных кавычек. Например:

"Something evil\n"\
"this way comes!"

int main()
{
    const char* text =
#include "file.txt"
;
    printf("%s", text);
    return 0;
}
2 голосов
/ 10 декабря 2012

Вам нужна моя xtr утилита, но вы можете сделать это с помощью bash script. Это сценарий, который я называю bin2inc. Первый параметр - это имя результирующего char[] variable. Второй параметр - это имя file. Выходные данные - C include file с содержимым файла, закодированным (строчными буквами hex) в качестве заданного имени переменной. char array равно zero terminated, а длина данных сохраняется в $variableName_length

#!/bin/bash

fileSize ()

{

    [ -e "$1" ]  && {

        set -- `ls -l "$1"`;

        echo $5;

    }

}

echo unsigned char $1'[] = {'
./xtr -fhex -p 0x -s ', ' < "$2";
echo '0x00'
echo '};';
echo '';
echo unsigned long int ${1}_length = $(fileSize "$2")';'

ВЫ МОЖЕТЕ ПОЛУЧИТЬ XTR ЗДЕСЬ xtr (символ eXTRapolator) - GPLV3

1 голос
/ 10 октября 2017

Я переопределил xxd в python3, исправив все неприятности xxd:

  • Const правильность
  • тип данных длины строки: int → size_t
  • Нулевое завершение (на случай, если вы захотите)
  • C совместимая строка: Drop unsigned в массиве.
  • Меньший, читаемый вывод, как вы написали бы: Printable ascii выводится как есть; другие байты в шестнадцатеричном коде.

Вот скрипт, отфильтрованный сам по себе, чтобы вы могли увидеть, что он делает:

pyxxd.c

#include <stddef.h>

extern const char pyxxd[];
extern const size_t pyxxd_len;

const char pyxxd[] =
"#!/usr/bin/env python3\n"
"\n"
"import sys\n"
"import re\n"
"\n"
"def is_printable_ascii(byte):\n"
"    return byte >= ord(' ') and byte <= ord('~')\n"
"\n"
"def needs_escaping(byte):\n"
"    return byte == ord('\\\"') or byte == ord('\\\\')\n"
"\n"
"def stringify_nibble(nibble):\n"
"    if nibble < 10:\n"
"        return chr(nibble + ord('0'))\n"
"    return chr(nibble - 10 + ord('a'))\n"
"\n"
"def write_byte(of, byte):\n"
"    if is_printable_ascii(byte):\n"
"        if needs_escaping(byte):\n"
"            of.write('\\\\')\n"
"        of.write(chr(byte))\n"
"    elif byte == ord('\\n'):\n"
"        of.write('\\\\n\"\\n\"')\n"
"    else:\n"
"        of.write('\\\\x')\n"
"        of.write(stringify_nibble(byte >> 4))\n"
"        of.write(stringify_nibble(byte & 0xf))\n"
"\n"
"def mk_valid_identifier(s):\n"
"    s = re.sub('^[^_a-z]', '_', s)\n"
"    s = re.sub('[^_a-z0-9]', '_', s)\n"
"    return s\n"
"\n"
"def main():\n"
"    # `xxd -i` compatibility\n"
"    if len(sys.argv) != 4 or sys.argv[1] != \"-i\":\n"
"        print(\"Usage: xxd -i infile outfile\")\n"
"        exit(2)\n"
"\n"
"    with open(sys.argv[2], \"rb\") as infile:\n"
"        with open(sys.argv[3], \"w\") as outfile:\n"
"\n"
"            identifier = mk_valid_identifier(sys.argv[2]);\n"
"            outfile.write('#include <stddef.h>\\n\\n');\n"
"            outfile.write('extern const char {}[];\\n'.format(identifier));\n"
"            outfile.write('extern const size_t {}_len;\\n\\n'.format(identifier));\n"
"            outfile.write('const char {}[] =\\n\"'.format(identifier));\n"
"\n"
"            while True:\n"
"                byte = infile.read(1)\n"
"                if byte == b\"\":\n"
"                    break\n"
"                write_byte(outfile, ord(byte))\n"
"\n"
"            outfile.write('\";\\n\\n');\n"
"            outfile.write('const size_t {}_len = sizeof({}) - 1;\\n'.format(identifier, identifier));\n"
"\n"
"if __name__ == '__main__':\n"
"    main()\n"
"";

const size_t pyxxd_len = sizeof(pyxxd) - 1;

Использование (это извлекает скрипт):

#include <stdio.h>

extern const char pyxxd[];
extern const size_t pyxxd_len;

int main()
{
    fwrite(pyxxd, 1, pyxxd_len, stdout);
}
1 голос
/ 14 сентября 2017

Вы можете сделать это, используя objcopy:

objcopy --input binary --output elf64-x86-64 myfile.txt myfile.o

Теперь у вас есть объектный файл, который вы можете связать в свой исполняемый файл, который содержит символы для начала, конца и размера содержимого от myfile.txt.

0 голосов
/ 04 марта 2019

Если вы готовы прибегнуть к некоторым грязным трюкам, вы можете проявить творческий подход к необработанным строковым литералам и #include для определенных типов файлов.

Например, скажем, я хочу включить некоторые сценарии SQL для SQLite в свой проект и хочу получить подсветку синтаксиса, но не хочу никакой специальной инфраструктуры сборки. Я могу иметь этот файл test.sql, который является допустимым SQL для SQLite, где -- начинает комментарий:

--x, R"(--
SELECT * from TestTable
WHERE field = 5
--)"

И тогда в моем коде C ++ я могу иметь:

int main()
{
    auto x = 0;
    const char* mysql = (
#include "test.sql"
    );

    cout << mysql << endl;
}

Вывод:

--
SELECT * from TestTable
WHERE field = 5
--

Или включить некоторый код Python из файла test.py, который является допустимым скриптом Python (потому что # запускает комментарий в Python, а pass - это запрет):

#define pass R"(
pass
def myfunc():
    print("Some Python code")

myfunc()
#undef pass
#define pass )"
pass

А затем в коде C ++:

int main()
{
    const char* mypython = (
#include "test.py"
    );

    cout << mypython << endl;
}

Который выдаст:

pass
def myfunc():
    print("Some Python code")

myfunc()
#undef pass
#define pass

Должна быть возможность воспроизводить подобные трюки для различных других типов кода, которые вы, возможно, захотите включить в виде строки. Является ли это хорошей идеей, я не уверен. Это что-то вроде аккуратного хака, но, вероятно, это не то, что вам нужно в реальном производственном коде. Возможно, все будет хорошо для хакерского проекта на выходных.

...