"#include " текстовый файл в программе на C как символ[]
есть ли способ включить весь текстовый файл в виде строки в программу на C во время компиляции?
что-то типа:
-
.txt:
This is a little text file
-
главная.c:
#include <stdio.h> int main(void) { #blackmagicinclude("file.txt", content) /* equiv: char[] content = "This isna littlentext file"; */ printf("%s", content); }
получение небольшой программы, которая печатает на stdout " это немного текстовый файл"
на данный момент я использовал хакерский скрипт python, но он уродлив и ограничен только одним именем переменной, можете ли вы мне сказать другой способ сделать это?
15 ответов:
Я бы предложил использовать (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;
вопрос был о 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)====="
у вас есть две возможности:
- используйте расширения компилятора/компоновщика для преобразования файла в двоичный файл с соответствующими символами, указывающими на начало и конец двоичных данных. Смотрите этот ответ:включить двоичный файл с GNU ld linker script.
- преобразуйте файл в последовательность символьных констант, которые могут инициализировать массив. Обратите внимание, что вы не можете просто сделать "" и охватить несколько строк. Вам понадобится символ продолжения строки (
\
), побег"
символы и другие, чтобы сделать эту работу. Проще просто написать небольшую программу для преобразования байтов в последовательность типа'\xFF', '\xAB', ...., ''
(или используйте инструмент unixxxd
описано другим ответом, если он у вас есть!):код:
#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" };
где данные.h генерируется с помощью
cat file.bin | ./bin2c > data.h
ОК, вдохновленный Дэмина сообщение Я проверил следующий простой пример:
а.данные:
"this is test\n file\n"
что может сработать, если вы сделаете что-то вроде:
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; }
тебе нужна моя
xtr
утилиты, но вы можете сделать это с помощьюbash script
. Это скрипт, который я называюbin2inc
. Первый параметр-это имя результирующегоchar[] variable
. Второй параметр-это имяfile
. На выходе сinclude file
с закодированным содержимым файла (в нижнем регистреhex
) в качестве имени переменной. Элементchar array
иzero terminated
, и длина данных хранится в$variableName_length
#!/bin/bash fileSize () { [ -e "" ] && { set -- `ls -l ""`; echo ; } } echo unsigned char '[] = {' ./xtr -fhex -p 0x -s ', ' < ""; echo '0x00' echo '};'; echo ''; echo unsigned long int _length = $(fileSize "")';'
ВЫ МОЖЕТЕ ПОЛУЧИТЬ XTR ЗДЕСЬ xtr (символ экстраполятор) - это GPLV3
вы можете сделать это с помощью
objcopy
:objcopy --input binary --output elf64-x86-64 myfile.txt myfile.o
Теперь у вас есть объектный файл, который вы можете связать с исполняемым файлом, который содержит символы для начала, конца и размера содержимого из
myfile.txt
.
мне нравится ответ Кайяра. Если вы не хотите прикасаться к входным файлам однако, и если вы используете 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, исправить все неприятности по ключам:
- Const корректность
- тип данных длины строки: int → size_t
- нулевое завершение (в случае, если вы этого хотите)
- C строка совместима: Drop
unsigned
в массиве.- меньший, читаемый вывод, как вы бы его написали: печатаемый 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); }
даже если это можно сделать во время компиляции (я не думаю, что это вообще возможно), текст, скорее всего, будет предварительно обработанным заголовком, а не содержимым файлов дословно. Я ожидаю, что вам придется загрузить текст из файла во время выполнения или сделать неприятную работу cut-n-paste.
в x. h
"this is a " "buncha text"
в Main.c
#include <stdio.h> int main(void) { char *textFileContents = #include "x.h" ; printf("%s\n", textFileContents); return 0 }
должен делать свою работу.
ответ Хастуркуна с использованием опции xxd-i превосходен. Если вы хотите включить процесс преобразования (text -> hex include file) непосредственно в вашу сборку hexdump.c tool / library недавно добавила возможность, аналогичную опции xxd-i (она не дает вам полный заголовок - Вам нужно предоставить определение массива символов - но это имеет то преимущество, что вы можете выбрать имя символа массив):
http://25thandclement.com / ~william/projects/hexdump.c.html
это лицензия намного более "стандартная", чем xxd, и очень либеральная - пример использования ее для встраивания файла init в программу можно увидеть в CMakeLists.txt и схема.c файлы здесь:
https://github.com/starseeker/tinyscheme-cmake
есть плюсы и минусы как для включения сгенерированных файлов в исходные деревья, так и для связывания утилит - как справиться с этим будет зависеть от конкретных целей и потребностей вашего проекта. шестнадцатеричного представления.c открывает опцию связывания для этого приложения.
Я думаю, что это невозможно только с компилятором и препроцессором. НКУ позволяет этого:
#define _STRGF(x) # x #define STRGF(x) _STRGF(x) printk ( MODULE_NAME " built " __DATE__ " at " __TIME__ " on host " STRGF( # define hostname my_dear_hostname hostname ) "\n" );
но к сожалению не этот:
#define _STRGF(x) # x #define STRGF(x) _STRGF(x) printk ( MODULE_NAME " built " __DATE__ " at " __TIME__ " on host " STRGF( # include "/etc/hostname" ) "\n" );
ошибка:
/etc/hostname: In function ‘init_module’: /etc/hostname:1:0: error: unterminated argument list invoking macro "STRGF"
Почему бы не связать текст в программу и использовать его в качестве глобальной переменной! вот пример. Я рассматриваю возможность использования этого для включения открытых файлов шейдеров GL в исполняемый файл, поскольку шейдеры GL должны быть скомпилированы для GPU во время выполнения.
у меня были похожие проблемы, и для небольших файлов вышеупомянутое решение Johannes Schaub работало как шарм для меня.
однако для файлов, которые немного больше, он столкнулся с проблемами с ограничением массива символов компилятора. Поэтому я написал небольшое приложение для кодирования, которое преобразует содержимое файла в массив 2D-символов с одинаковыми размерами кусков (и, возможно, заполнение нулей). Он производит выходные текстовые файлы с данными 2D массива следующим образом:
const char main_js_file_data[8][4]= { {'\x69','\x73','\x20',''}, {'\x69','\x73','\x20',''}, {'\x61','\x20','\x74',''}, {'\x65','\x73','\x74',''}, {'\x20','\x66','\x6f',''}, {'\x72','\x20','\x79',''}, {'\x6f','\x75','\xd',''}, {'\xa','','',''}};
где 4 на самом деле является переменной MAX_CHARS_PER_ARRAY в кодере. Файл с полученным кодом C, называется, например " main_js_file_data.затем h " можно легко встроить в приложение C++, например, так:
#include "main_js_file_data.h"
вот исходный код кодировщика:
#include <fstream> #include <iterator> #include <vector> #include <algorithm> #define MAX_CHARS_PER_ARRAY 2048 int main(int argc, char * argv[]) { // three parameters: input filename, output filename, variable name if (argc < 4) { return 1; } // buffer data, packaged into chunks std::vector<char> bufferedData; // open input file, in binary mode { std::ifstream fStr(argv[1], std::ios::binary); if (!fStr.is_open()) { return 1; } bufferedData.assign(std::istreambuf_iterator<char>(fStr), std::istreambuf_iterator<char>() ); } // write output text file, containing a variable declaration, // which will be a fixed-size two-dimensional plain array { std::ofstream fStr(argv[2]); if (!fStr.is_open()) { return 1; } const std::size_t numChunks = std::size_t(std::ceil(double(bufferedData.size()) / (MAX_CHARS_PER_ARRAY - 1))); fStr << "const char " << argv[3] << "[" << numChunks << "]" << "[" << MAX_CHARS_PER_ARRAY << "]= {" << std::endl; std::size_t count = 0; fStr << std::hex; while (count < bufferedData.size()) { std::size_t n = 0; fStr << "{"; for (; n < MAX_CHARS_PER_ARRAY - 1 && count < bufferedData.size(); ++n) { fStr << "'\x" << int(unsigned char(bufferedData[count++])) << "',"; } // fill missing part to reach fixed chunk size with zero entries for (std::size_t j = 0; j < (MAX_CHARS_PER_ARRAY - 1) - n; ++j) { fStr << "'\0',"; } fStr << "'\0'}"; if (count < bufferedData.size()) { fStr << ",\n"; } } fStr << "};\n"; } return 0; }