"#include" текстовый файл в программе на C как char []

Есть ли способ включить весь текстовый файл в виде строки в программу 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, но он уродливый и ограничен только одним именем переменной, вы можете подсказать мне другой способ сделать это?

почему ты хочешь сделать это? Почему бы не прочитать файл во время выполнения? (Ответ: возможно, потому, что трудно узнать, где находится файл во время выполнения, или, может быть, потому, что должен быть только один файл для установки.)

Jonathan Leffler 04.01.2009 19:43

Посмотрите здесь, чтобы прочитать файл в 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

Daniel A. White 04.01.2009 16:41

или, возможно, текстовый файл доступен только во время компиляции, например, исходный код.

TMS 21.09.2018 18:11

Иногда вы хотите получить доступ к данным как к отдельным файлам во время разработки, но при этом содержимое будет скомпилировано в ваш двоичный файл. Пример запускает веб-сервер на Arduino, у которого нет доступа к локальному хранилищу. Вы хотите, чтобы ваши html-файлы были отдельными, чтобы редактировать их, но во время компиляции они должны существовать в виде строк в вашем источнике.

Geordie 27.01.2019 11:43
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
138
4
69 942
19
Перейти к ответу Данный вопрос помечен как решенный

Ответы 19

Как работает мощь, если вы сделаете что-то вроде:

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 04.01.2009 16:59

@Motti: согласовано - как написано, синтаксически неверно C. Идея интересна - препроцессор C логически является отдельной фазой, но практика такова, что он не запускается, потому что каждая строка во включенном файле будет иметь заканчиваться обратной косой чертой и т. д.

Jonathan Leffler 04.01.2009 19:39

Хммм. Мне кажется, вам не нужна обратная косая черта, поскольку большинство компиляторов объединяют смежные строки вместе

EvilTeach 05.01.2009 01:00

дело в этом ответе ... если бы это было так просто, я не думаю, что OP когда-либо задал бы этот вопрос! -1, потому что наличие этого ответа немного побуждает людей тратить время на попытки чего-то, что не работает. Я думаю, что мы могли бы удалить отрицательный голос, если бы вы изменили "Что может сработать" на "Для справки, это не работает".

Mark Ch 05.07.2019 11:01

@JonathanLeffler После запуска препроцессора он должен быть допустимым для C или C++, в зависимости от формата file.txt.

Daemin 26.07.2019 11:25

@MarkCh Это так просто, другой ответ от ilya выше подтвердил это и делает то же самое, что и мой второй фрагмент кода.

Daemin 26.07.2019 11:27

Даже если это можно сделать во время компиляции (я не думаю, что это вообще возможно), текст, скорее всего, будет предварительно обработанным заголовком, а не дословно содержимым файлов. Я ожидаю, что вам придется загрузить текст из файла во время выполнения или выполнить неприятную работу вырезания и вставки.

Ответ принят как подходящий

Я бы предложил использовать для этого (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"};

ZeD 04.01.2009 19:10

Я никогда не знал про xxd. Это круто!

anon 06.01.2009 03:29

@Hasturkun: Я понимаю, как вы сгенерировали вывод с помощью xxd. Я не понимаю, как вы собираетесь включать xxd в свой код C. В конце концов, xxd - это команда оболочки. Как вы собираетесь использовать его в программе на языке C ??

Lazer 20.03.2010 14:33

@eSKay: вы не включаете xxd в свой код, вы включаете вывод xxd в свой код. например. вы можете запустить что-то вроде xxd -i inputfile outputfile.h и более поздних версий #include "outputfile.h"

Hasturkun 21.03.2010 21:27

@Hasturkun: Как вы получите детали unsigned char a[] = { и }?

Lazer 21.03.2010 21:42

@eSKay: это происходит непосредственно из вывода xxd, как сказано в ответе. имя массива - это имя входного файла. если вы передаете данные по конвейеру вместо использования входного файла, вместо этого вы получите список шестнадцатеричных значений (без объявления массива или переменной len).

Hasturkun 22.03.2010 02:42

Это чрезвычайно полезно при встраивании шейдеров GLSL.

linello 18.01.2016 14:11

Другой способ добавить завершение 0x00 в код C, созданный xxd: xxd -i file.txt | sed 's/\([0-9a-f]\)$/\0, 0x00/' > file.h

vleo 26.02.2016 20:36

Это хорошо работает в autoconf configure.ac, но если вы выводите квадратные скобки, помните, что первым должен быть [[для экранирования autoconf.

Jack Wasey 11.04.2019 18:54

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

  1. Используйте расширения компилятора / компоновщика для преобразования файла в двоичный файл с правильными символами, указывающими на начало и конец двоичных данных. См. Этот ответ: Включите двоичный файл со сценарием компоновщика GNU ld.
  2. Преобразуйте ваш файл в последовательность символьных констант, которые могут инициализировать массив. Обратите внимание, что вы не можете просто сделать "" и охватить несколько строк. Вам понадобится символ продолжения строки (\), 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"

Hasturkun 04.01.2009 17:08

Я использовал codeproject.com/Tips/845393/… для создания шестнадцатеричного файла (в Windows) из двоичного файла, а затем использовал ваше предложение char my_file[] = { #include my_large_file.h };. Спасибо!

Someone Somewhere 05.01.2016 15:10
bin2c - это нет, тот же bin2c, что и hxtools от debian, будьте осторожны
ThorSummoner 05.10.2019 09:32

или если это так, вызов теперь намного более странный: bin2c -H myoutput.h myinput1.txt myinputN.txt

ThorSummoner 05.10.2019 09:45

хорошо, вдохновленный постом Дэмина, я протестировал следующий простой пример:

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

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

Это то, что я имел в виду в последней части своего ответа.

Daemin 05.01.2009 01:11

цитата, или как там это называется, простите за мой английский

Ilya 29.07.2009 19:32

Это требует, чтобы данные были экранированы C. Я не думаю, что это то, что ищет пост. Если бы у него был какой-то включаемый макрос, который C-экранировал содержимое файла, это было бы хорошо.

Brian Chrisman 10.08.2019 19:08

в 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"

Superfly Jon 27.09.2016 13:46

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

Mark Ch 05.07.2019 11:56

Вам понадобится моя утилита 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 ™.

Jonathan Leffler 15.04.2014 18:26

Вопрос касался 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 на отдельные файлы и редактировать их с помощью соответствующей проверки синтаксиса, выделения и т. д.

YitzikC 30.01.2017 00:58

Это действительно близко к тому, что я хочу. Особенно определяемый пользователем разделитель. Очень полезно. Я хочу пойти еще дальше: есть ли способ полностью удалить префикс R "(и суффикс)" из файла, который вы хотите включить? Я попытался определить два файла с именами bra.in и ket.in с префиксом и суффиксом в них, включая bra.in, file.txt и ket.in один за другим. Но компилятор оценивает содержимое bra.in (это просто R "() перед включением следующего файла. Так что он будет жаловаться. Пожалуйста, дайте мне знать, если кто-нибудь знает, как получить префикс и суффикс из file.txt. Спасибо.

TMS 21.09.2018 07:16

Я предполагаю, что C++ не позволит R "(<newline> #include ...)"? Было бы неплохо, если бы файл загружался во время компиляции, чтобы не требовать никакой кодировки ... то есть прямо json, xml или csv или что-то еще ...

Brian Chrisman 10.08.2019 19:09

Вы можете сделать текст необработанного литерала более читабельным, если вы используете 1+R"... в качестве начального разделителя вместо R"..., а затем добавите новую строку перед Line 1. Это преобразует выражение из массива в указатель, но это не проблема, поскольку вы инициализируете указатель, а не массив.

Ruslan 01.01.2020 20:39

Почему бы не связать текст с программой и не использовать его как глобальную переменную! Вот пример. Я рассматриваю возможность использования этого для включения файлов шейдеров 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.

Вы можете сказать нам, какие будут названия символов?

Mark Ch 05.07.2019 11:58

@MarkCh: согласно документации, имена символов генерируются из имени входного файла.

John Zwinck 05.07.2019 13:59

Я предполагаю, что это не будет работать на машинах, отличных от x86-64, не так ли?

ThorSummoner 05.10.2019 09:39

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

  • Постоянная правильность
  • длина строки тип данных: int → size_t
  • Нулевое завершение (на случай, если вы этого захотите)
  • Совместимость со строкой C: Поместите 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);
}

Мне нравится ответ Каяра. Однако Если вы не хотите трогать входные файлы, и если вы используете 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"
;

Отлично. Спасибо +

Ilya Polishchuk 04.10.2020 16:48

Люблю этот ответ!

avee 01.01.2021 10:19

Если вы готовы прибегнуть к каким-то грязным уловкам, вы можете проявить творческий подход с необработанными строковыми литералами и #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 в текстовые файлы!

yano 05.08.2019 19:51

Эта проблема меня раздражала, и 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 ^ в конце, но это не проблема, и мне нравится простота этого решения. Если кто-то может уточнить, чтобы избавиться от лишних символов, это было бы хорошо.

Другие вопросы по теме