Есть ли способ включить весь текстовый файл в виде строки в программу 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);
}
получение небольшой программы, которая печатает на stdout "Это маленький текстовый файл"
На данный момент я использовал хакерский скрипт на python, но он уродливый и ограничен только одним именем переменной, вы можете подсказать мне другой способ сделать это?
Посмотрите здесь, чтобы прочитать файл в char []. https://stackoverflow.com/questions/410943/reading-a-text-fi le-into-an-array-in- c Вот несколько советов по использованию макросов препроцессора C. http://gcc.gnu.org/onlinedocs/cpp/Macros.html
или, возможно, текстовый файл доступен только во время компиляции, например, исходный код.
Иногда вы хотите получить доступ к данным как к отдельным файлам во время разработки, но при этом содержимое будет скомпилировано в ваш двоичный файл. Пример запускает веб-сервер на Arduino, у которого нет доступа к локальному хранилищу. Вы хотите, чтобы ваши html-файлы были отдельными, чтобы редактировать их, но во время компиляции они должны существовать в виде строк в вашем источнике.





Как работает мощь, если вы сделаете что-то вроде:
int main()
{
const char* text = "
#include "file.txt"
";
printf("%s", text);
return 0;
}
Конечно, вам придется будьте осторожны с тем, что на самом деле находится в файле, убедившись, что нет двойных кавычек, что все соответствующие символы экранированы и т. д.
Поэтому было бы проще, если бы вы просто загрузите текст из файла во время выполнения или встроили текст прямо в код.
Если вам по-прежнему нужен текст в другом файле, он может быть там, но он должен быть представлен там в виде строки. Вы должны использовать код, как указано выше, но без двойных кавычек. Например:
file.txt
"Something evil\n"\
"this way comes!"
main.cpp
int main()
{
const char* text =
#include "file.txt"
;
printf("%s", text);
return 0;
}
Итак, в основном это строка стиля C или C++ в текстовом файле, который вы включаете. Это сделало бы код более аккуратным, потому что в начале файла не так много текста.
Хорошая идея, но это не сработает, либо у вас есть ошибка, потому что литерал включает новую строку, либо часть #include будет прочитана как строка и не будет выполнена, черт возьми, если вы это сделаете, и проклят, если вы этого не сделаете ... .
@Motti: согласовано - как написано, синтаксически неверно C. Идея интересна - препроцессор C логически является отдельной фазой, но практика такова, что он не запускается, потому что каждая строка во включенном файле будет иметь заканчиваться обратной косой чертой и т. д.
Хммм. Мне кажется, вам не нужна обратная косая черта, поскольку большинство компиляторов объединяют смежные строки вместе
дело в этом ответе ... если бы это было так просто, я не думаю, что OP когда-либо задал бы этот вопрос! -1, потому что наличие этого ответа немного побуждает людей тратить время на попытки чего-то, что не работает. Я думаю, что мы могли бы удалить отрицательный голос, если бы вы изменили "Что может сработать" на "Для справки, это не работает".
@JonathanLeffler После запуска препроцессора он должен быть допустимым для C или C++, в зависимости от формата file.txt.
@MarkCh Это так просто, другой ответ от ilya выше подтвердил это и делает то же самое, что и мой второй фрагмент кода.
Даже если это можно сделать во время компиляции (я не думаю, что это вообще возможно), текст, скорее всего, будет предварительно обработанным заголовком, а не дословно содержимым файлов. Я ожидаю, что вам придется загрузить текст из файла во время выполнения или выполнить неприятную работу вырезания и вставки.
Я бы предложил использовать для этого (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;
Просто примечание: char [], созданный xxd, не завершается NULL! поэтому я делаю $ xxd -i <file.txt> file.xxd $ echo ', 0' >> file.xxd и в main.c char file_content [] = {#include "file.xxd"};
Я никогда не знал про xxd. Это круто!
@Hasturkun: Я понимаю, как вы сгенерировали вывод с помощью xxd. Я не понимаю, как вы собираетесь включать xxd в свой код C. В конце концов, xxd - это команда оболочки. Как вы собираетесь использовать его в программе на языке C ??
@eSKay: вы не включаете xxd в свой код, вы включаете вывод xxd в свой код. например. вы можете запустить что-то вроде xxd -i inputfile outputfile.h и более поздних версий #include "outputfile.h"
@Hasturkun: Как вы получите детали unsigned char a[] = { и }?
@eSKay: это происходит непосредственно из вывода xxd, как сказано в ответе. имя массива - это имя входного файла. если вы передаете данные по конвейеру вместо использования входного файла, вместо этого вы получите список шестнадцатеричных значений (без объявления массива или переменной len).
Это чрезвычайно полезно при встраивании шейдеров GLSL.
Другой способ добавить завершение 0x00 в код C, созданный xxd: xxd -i file.txt | sed 's/\([0-9a-f]\)$/\0, 0x00/' > file.h
Это хорошо работает в autoconf configure.ac, но если вы выводите квадратные скобки, помните, что первым должен быть [[для экранирования autoconf.
У вас есть две возможности:
\), escape-символы " и другие, чтобы это работало. Проще просто написать небольшую программу для преобразования байтов в последовательность вроде '\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
последняя строка, вероятно, должна читать "cat file.bin | ./bin2c> data.h" или "./bin2c <file.bin> data.h"
Я использовал codeproject.com/Tips/845393/… для создания шестнадцатеричного файла (в Windows) из двоичного файла, а затем использовал ваше предложение char my_file[] = { #include my_large_file.h };. Спасибо!
bin2c - это нет, тот же bin2c, что и hxtools от debian, будьте осторожны
или если это так, вызов теперь намного более странный: bin2c -H myoutput.h myinput1.txt myinputN.txt
хорошо, вдохновленный постом Дэмина, я протестировал следующий простой пример:
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;
}
Так что это работает, но требуются данные, заключенные в кавычки.
Это то, что я имел в виду в последней части своего ответа.
цитата, или как там это называется, простите за мой английский
Это требует, чтобы данные были экранированы C. Я не думаю, что это то, что ищет пост. Если бы у него был какой-то включаемый макрос, который C-экранировал содержимое файла, это было бы хорошо.
в 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
}
должен делать свою работу.
Для нескольких строк вам нужно добавить \ n так: "строка 1 \ n" "строка 2 \ n"
это немного вводит в заблуждение, очевидно, это требует некоторой подготовки текстового файла для добавления кавычек и символов \ n, не работает в общем случае
Вам понадобится моя утилита 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
Ответ Хастуркуна с использованием опции xxd -i превосходен. Если вы хотите включить процесс преобразования (текст -> шестнадцатеричный включаемый файл) непосредственно в вашу сборку, инструмент / библиотека hexdump.c недавно добавили возможность, аналогичную опции -i xxd (она не дает вам полный заголовок - вам нужен чтобы предоставить определение массива символов, но это имеет то преимущество, что вы можете выбрать имя массива символов):
http://25thandclement.com/~william/projects/hexdump.c.html
Эта лицензия намного более "стандартна", чем xxd, и очень либеральна - пример ее использования для встраивания файла инициализации в программу можно увидеть в файлах CMakeLists.txt и scheme.c здесь:
https://github.com/starseeker/tinyscheme-cmake
Есть свои плюсы и минусы как для включения сгенерированных файлов в исходные деревья, так и для пакетных утилит - как с этим справиться, зависит от конкретных целей и потребностей вашего проекта. hexdump.c открывает опцию связывания для этого приложения.
Я думаю, что это невозможно только с компилятором и препроцессором. gcc позволяет это:
#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"
Я смотрел, как ты велел мне смотреть. Я не вижу в вашем ответе новой информации (информации, которой нет в других ответах), кроме ссылки на /etc/hostname как способ встраивания имени машины сборки в строку, которая (даже если бы она сработала) не быть переносимым, поскольку в Mac OS X нет файла /etc/hostname. Обратите внимание, что при использовании имен макросов, которые начинаются с подчеркивания, за которым следует заглавная буква, используется имя, зарезервированное для реализации, а это A Bad Thing ™.
Вопрос касался 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)==== = "
Спасибо, я выбрал предложенный здесь метод для встраивания длинных фрагментов sql в мой код C++ 11. Это позволяет мне четко разделять SQL на отдельные файлы и редактировать их с помощью соответствующей проверки синтаксиса, выделения и т. д.
Это действительно близко к тому, что я хочу. Особенно определяемый пользователем разделитель. Очень полезно. Я хочу пойти еще дальше: есть ли способ полностью удалить префикс R "(и суффикс)" из файла, который вы хотите включить? Я попытался определить два файла с именами bra.in и ket.in с префиксом и суффиксом в них, включая bra.in, file.txt и ket.in один за другим. Но компилятор оценивает содержимое bra.in (это просто R "() перед включением следующего файла. Так что он будет жаловаться. Пожалуйста, дайте мне знать, если кто-нибудь знает, как получить префикс и суффикс из file.txt. Спасибо.
Я предполагаю, что C++ не позволит R "(<newline> #include ...)"? Было бы неплохо, если бы файл загружался во время компиляции, чтобы не требовать никакой кодировки ... то есть прямо json, xml или csv или что-то еще ...
Вы можете сделать текст необработанного литерала более читабельным, если вы используете 1+R"... в качестве начального разделителя вместо R"..., а затем добавите новую строку перед Line 1. Это преобразует выражение из массива в указатель, но это не проблема, поскольку вы инициализируете указатель, а не массив.
Почему бы не связать текст с программой и не использовать его как глобальную переменную! Вот пример. Я рассматриваю возможность использования этого для включения файлов шейдеров Open GL в исполняемый файл, поскольку шейдеры GL необходимо компилировать для графического процессора во время выполнения.
У меня были похожие проблемы, и для небольших файлов вышеупомянутое решение Йоханнеса Шауба подействовало на меня как шарм.
Однако для файлов, которые немного больше, возникали проблемы с ограничением массива символов компилятора. Поэтому я написал небольшое приложение-кодировщик, которое преобразует содержимое файла в массив 2D-символов, состоящий из блоков одинакового размера (и, возможно, дополняющих нулей). Он создает выходные текстовые файлы с данными 2D-массива, например:
const char main_js_file_data[8][4]= {
{'\x69','\x73','\x20','\0'},
{'\x69','\x73','\x20','\0'},
{'\x61','\x20','\x74','\0'},
{'\x65','\x73','\x74','\0'},
{'\x20','\x66','\x6f','\0'},
{'\x72','\x20','\x79','\0'},
{'\x6f','\x75','\xd','\0'},
{'\xa','\0','\0','\0'}};
где 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;
}
Вы можете сделать это с помощью objcopy:
objcopy --input binary --output elf64-x86-64 myfile.txt myfile.o
Теперь у вас есть объектный файл, который вы можете связать со своим исполняемым файлом, который содержит символы для начала, конца и размера содержимого из myfile.txt.
Вы можете сказать нам, какие будут названия символов?
@MarkCh: согласно документации, имена символов генерируются из имени входного файла.
Я предполагаю, что это не будет работать на машинах, отличных от x86-64, не так ли?
Я повторно реализовал xxd в python3, устранив все неприятности xxd:
unsigned в массив.Вот сценарий, отфильтрованный сам по себе, чтобы вы могли видеть, что он делает:
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);
}
Мне нравится ответ Каяра. Однако Если вы не хотите трогать входные файлы, и если вы используете 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"
;
Отлично. Спасибо +
Люблю этот ответ!
Если вы готовы прибегнуть к каким-то грязным уловкам, вы можете проявить творческий подход с необработанными строковыми литералами и #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
Должна быть возможность использовать аналогичные приемы для различных других типов кода, которые вы, возможно, захотите включить в виде строки. Я не уверен, хорошая это идея или нет. Это своего рода изящный прием, но, вероятно, не то, что вам нужно в реальном производственном коде. Впрочем, для хакерского проекта на выходных может подойти.
Я использовал этот подход, чтобы поместить шейдеры OpenGL в текстовые файлы!
Эта проблема меня раздражала, и xxd не работает для моего варианта использования, потому что он сделал переменную, названную чем-то вроде __home_myname_build_prog_cmakelists_src_autogen, когда я попытался ее написать, поэтому я сделал утилиту для решения этой точной проблемы:
https://github.com/Exaeta/brcc
Он генерирует исходный файл и файл заголовка и позволяет вам явно указать имя каждой переменной, чтобы затем вы могли использовать их через std :: begin (имя массива) и std :: end (имя массива).
Я включил его в свой проект cmake вот так:
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/binary_resources.hpp ${CMAKE_CURRENT_BINARY_DIR}/binary_resources.cpp
COMMAND brcc ${CMAKE_CURRENT_BINARY_DIR}/binary_resources RGAME_BINARY_RESOURCES_HH txt_vertex_shader ${CMAKE_CURRENT_BINARY_DIR}/src/vertex_shader1.glsl
DEPENDS src/vertex_shader1.glsl)
Я полагаю, что с небольшими изменениями его можно было бы заставить работать и для C.
Если вы используете CMake, вам, вероятно, будет интересно написать сценарий предварительной обработки CMake, подобный следующему:
cmake / ConvertLayout.cmake
function(convert_layout file include_dir)
get_filename_component(name ${file} NAME_WE)
get_filename_component(directory ${file} DIRECTORY)
get_filename_component(directory ${directory} NAME)
string(TOUPPER ${name} NAME)
string(TOUPPER ${directory} DIRECTORY)
set(new_file ${include_dir}/${directory}/${name}.h)
if (${file} IS_NEWER_THAN ${new_file})
file(READ ${file} content)
string(REGEX REPLACE "\"" "\\\\\"" content "${content}")
string(REGEX REPLACE "[\r\n]" "\\\\n\"\\\\\n\"" content "${content}")
set(content "\"${content}\"")
set(content "#ifndef ${DIRECTORY}_${NAME}\n#define ${DIRECTORY}_${NAME} ${content} \n#endif")
message(STATUS "${content}")
file(WRITE ${new_file} "${content}")
message(STATUS "Generated layout include file ${new_file} from ${file}")
endif ()
endfunction()
function(convert_layout_directory layout_dir include_dir)
file(GLOB layouts ${layout_dir}/*)
foreach(layout ${layouts})
convert_layout(${layout} ${include_dir})
endforeach()
endfunction()
ваш CMakeLists.txt
include(cmake/ConvertLayout.cmake)
convert_layout_directory(layout ${CMAKE_BINARY_DIR}/include)
include_directories(${CMAKE_BINARY_DIR}/include)
где-то в C++
#include "layout/menu.h"
Glib::ustring ui_info = LAYOUT_MENU;
Вот хак, который я использую для Visual C++. Я добавляю следующее событие предварительной сборки (где file.txt - это вход, а file_txt.h - выход):
@(
echo const char text[] = R"***(
type file.txt
echo ^^^)***";
) > file_txt.h
Затем я включаю file_txt.h туда, где мне это нужно.
Это не идеально, так как добавляется \ n в начале и \ n ^ в конце, но это не проблема, и мне нравится простота этого решения. Если кто-то может уточнить, чтобы избавиться от лишних символов, это было бы хорошо.
почему ты хочешь сделать это? Почему бы не прочитать файл во время выполнения? (Ответ: возможно, потому, что трудно узнать, где находится файл во время выполнения, или, может быть, потому, что должен быть только один файл для установки.)