„#Include” plik tekstowy w programie w języku C jako znak []


132

Czy istnieje sposób na dołączenie całego pliku tekstowego jako ciągu w programie C w czasie kompilacji?

coś jak:

  • plik.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);
    }
    

uzyskanie małego programu, który drukuje na stdout "To jest mały plik tekstowy"

W tej chwili użyłem hakerskiego skryptu w Pythonie, ale jest on brzydki i ogranicza się do jednej nazwy zmiennej, czy możesz mi powiedzieć inny sposób, aby to zrobić?


Spójrz tutaj, aby wczytać plik do znaku []. /programming/410943/reading-a-text-file-into-an-array-in-c Oto kilka wskazówek dotyczących używania makr preprocesora C. http://gcc.gnu.org/onlinedocs/cpp/Macros.html
Daniel A. White

3
Dlaczego chcesz to zrobić? Dlaczego nie przeczytać pliku w czasie wykonywania? (Odpowiedź: może dlatego, że trudno jest wiedzieć, gdzie jest plik w czasie wykonywania, a może dlatego, że powinien być tylko jeden plik do zainstalowania.)
Jonathan Leffler

1
lub może plik tekstowy jest dostępny tylko w czasie kompilacji, na przykład kod źródłowy.
TMS

2
Czasami chcesz uzyskać dostęp do danych jako oddzielnych plików w czasie programowania, ale zawartość wkompilowana w plik binarny. Przykładem jest uruchomienie serwera WWW na Arduino, który nie ma dostępu do lokalnej pamięci. Chcesz zachować oddzielne pliki html, aby je edytować, ale w czasie kompilacji muszą istnieć jako ciągi w twoim źródle.
Geordie

Odpowiedzi:


137

Sugerowałbym użycie do tego (unix util) xxd . możesz go tak używać

$ echo hello world > a
$ xxd -i a

wyjścia:

unsigned char a[] = {
  0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x0a
};
unsigned int a_len = 12;

20
Uwaga: znak [] utworzony przez xxd nie jest zakończony znakiem NULL! więc robię $ xxd -i <file.txt> file.xxd $ echo ', 0' >> file.xxd i w main.c char file_content [] = {#include "file.xxd"};

3
Nigdy nie wiedziałem o XXD. To jest zajebiste!

1
@eSKay: pochodzi bezpośrednio z wyniku xxd, jak mówi odpowiedź. nazwą tablicy jest nazwa pliku wejściowego. jeśli przepuszczasz dane zamiast korzystać z pliku wejściowego, zamiast tego otrzymasz listę wartości szesnastkowych (bez deklaracji tablicy lub zmiennej len).
Hasturkun

4
Jest to niezwykle przydatne podczas osadzania shaderów GLSL.
linello

5
Inny sposób dodania zakończenia 0x00 do utworzonego kodu C xxd:xxd -i file.txt | sed 's/\([0-9a-f]\)$/\0, 0x00/' > file.h
vleo

109

Pytanie dotyczyło C, ale na wypadek, gdyby ktoś próbował to zrobić w C ++ 11, można to zrobić z niewielkimi zmianami w dołączonym pliku tekstowym dzięki nowym surowym literałom ciągów :

W C ++ zrób to:

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

W pliku tekstowym zrób to:

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

Zatem musi znajdować się tylko przedrostek na początku pliku i przyrostek na końcu. Między nim możesz robić, co chcesz, nie jest konieczne specjalne uciekanie, o ile nie potrzebujesz sekwencji znaków )". Ale nawet to może działać, jeśli określisz własny niestandardowy separator:

R"=====(Line 1
Line 2
Line 3
Now you can use "( and )" in the text file, too.
Line 5
Line 6)====="

5
Dzięki, wybrałem proponowaną tutaj metodę osadzania długich fragmentów sql w moim kodzie C ++ 11. To pozwala mi zachować czytelny podział SQL na własne pliki i edytować je z odpowiednim sprawdzaniem składni, podświetlaniem itp.
YitzikC

2
To jest naprawdę blisko tego, czego chcę. W szczególności ogranicznik zdefiniowany przez użytkownika. Bardzo przydatne. Chcę pójść o krok dalej: czy istnieje sposób na całkowite usunięcie przedrostka R „(i sufiksu)” z pliku, który chcesz dołączyć? Próbowałem zdefiniować dwa pliki o nazwach bra.in i ket.in z prefiksem i sufiksem, w tym bra.in, plik.txt i ket.in jeden po drugim. Ale kompilator ocenia zawartość bra.in (która jest po prostu R "() przed dołączeniem następnego pliku. Więc będzie narzekać. Daj mi znać, jeśli ktoś wie, jak uzyskać jazdę z prefiksem i sufiksem z pliku.txt. Dzięki.
TMS

Domyślam się, że C ++ nie pozwoliłby R "(<newline> #include ...)"? Byłoby miło, gdyby plik był przetwarzany w czasie kompilacji, aby nie wymagał żadnego kodowania… tj. Prosty json lub xml lub csv lub co nie…
Brian Chrisman

Możesz uczynić tekst surowego literału trochę bardziej czytelnym, jeśli użyjesz go 1+R"...jako początkowego separatora zamiast R"..., a następnie wstawisz przed nim nowy wiersz Line 1. Spowoduje to przekształcenie wyrażenia z tablicy w wskaźnik, ale nie stanowi to problemu, ponieważ inicjalizujesz wskaźnik, a nie tablicę.
Rusłan

14

Masz dwie możliwości:

  1. Skorzystaj z rozszerzeń kompilatora / konsolidatora, aby przekonwertować plik na plik binarny, z odpowiednimi symbolami wskazującymi na początek i koniec danych binarnych. Zobacz tę odpowiedź: Dołącz plik binarny do skryptu konsolidującego GNU ld .
  2. Przekonwertuj plik na sekwencję stałych znakowych, które mogą zainicjować tablicę. Zauważ, że nie możesz po prostu zrobić „” i rozciągnąć się na wiele linii. Potrzebowałbyś znaku kontynuacji linii ( \), "znaków zmiany znaczenia i innych, aby to zadziałało. Łatwiej jest po prostu napisać mały program do konwersji bajtów na sekwencję, taką jak '\xFF', '\xAB', ...., '\0'(lub użyj narzędzia uniksowego xxdopisanego w innej odpowiedzi, jeśli masz je dostępne!):

Kod:

#include <stdio.h>

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

(nie testowany). Następnie wykonaj:

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

Gdzie data.h jest generowana przez

cat file.bin | ./bin2c > data.h

1
Ostatnia linia prawdopodobnie powinien przeczytać "koci file.bin | ./bin2c> data.h" lub "./bin2c <file.bin> data.h"
Hasturkun

Użyłem codeproject.com/Tips/845393/ ... do utworzenia pliku szesnastkowego (w systemie Windows) z pliku binarnego, a następnie skorzystałem z Twojej sugestii char my_file[] = { #include my_large_file.h };dzięki!
Someone Somewhere

bin2cnie jest tym samym bin2c co z debiana hxtools, uważaj
ThorSummoner

a jeśli tak, to wywołanie jest teraz znacznie dziwniejsze:bin2c -H myoutput.h myinput1.txt myinputN.txt
ThorSummoner

9

ok, zainspirowany postem Daemina przetestowałem następujący prosty przykład:

a. dane:

"this is test\n file\n"

test.c:

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

Wyjście 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;
}

Więc to działa, ale wymaga danych w cudzysłowie.


Właśnie do tego nawiązałem w ostatniej części mojej odpowiedzi.
Daemin

cytat, czy jakkolwiek to się nazywa, wybacz mój angielski
Ilya

Wymaga to zmiany znaczenia danych w C. Nie sądzę, że tego właśnie szukał post. Gdyby to miało jakieś makro włączające, które uciekało z C do zawartości pliku, byłoby dobrze.
Brian Chrisman

9

Podoba mi się odpowiedź Kayahra. Jeśli jednak nie chcesz dotykać plików wejściowych i używasz CMake , możesz dodać sekwencje znaków separatora do pliku. Na przykład następujący kod CMake kopiuje pliki wejściowe i odpowiednio opakowuje ich zawartość:

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)

Następnie dołącz w c ++ w ten sposób:

constexpr char *test =
#include "generated/cool.frag"
;

Świetny. Dziękuję +
Ilya Polishchuk

5

Możesz to zrobić za pomocą objcopy:

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

Teraz masz plik obiektowy, który możesz połączyć z plikiem wykonywalnym, który zawiera symbole początku, końca i rozmiaru treści myfile.txt.


1
czy możesz nam powiedzieć, jakie będą nazwy symboli?
Mark Ch,

@MarkCh: zgodnie z dokumentacją nazwy symboli są generowane na podstawie nazwy pliku wejściowego.
John Zwinck,

Zgaduję, że to nie zadziała na komputerach innych niż x86-64, prawda?
ThorSummoner


3

Jeśli chcesz uciec się do brudnych sztuczek, możesz wykazać się kreatywnością dzięki surowym literałom tekstowym i #includeniektórym typom plików.

Na przykład, powiedzmy, że chcę dołączyć do projektu niektóre skrypty SQL dla SQLite i chcę uzyskać podświetlanie składni, ale nie chcę żadnej specjalnej infrastruktury kompilacji. Mogę mieć ten plik, test.sqlktóry jest prawidłowym kodem SQL dla SQLite, gdzie --zaczyna się komentarz:

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

A potem w moim kodzie C ++ mogę mieć:

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

    cout << mysql << endl;
}

Wynik to:

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

Lub dołączyć kod w Pythonie z pliku, test.pyktóry jest prawidłowym skryptem w Pythonie (ponieważ #rozpoczyna komentarz w Pythonie i nie passdziała):

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

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

A potem w kodzie C ++:

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

    cout << mypython << endl;
}

Który wyświetli:

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

myfunc()
#undef pass
#define pass

Powinno być możliwe wykonywanie podobnych sztuczek dla różnych innych typów kodu, które możesz chcieć dołączyć jako ciąg. Nie jestem pewien, czy to dobry pomysł. To trochę fajny hack, ale prawdopodobnie nie jest to coś, czego chciałbyś w prawdziwym kodzie produkcyjnym. Może być jednak ok na weekendowy projekt hakerski.


Użyłem tego podejścia, aby umieścić shadery OpenGL w plikach tekstowych!
yano

2

Potrzebujesz mojego xtrnarzędzia, ale możesz to zrobić za pomocą pliku bash script. To jest skrypt, który nazywambin2inc . Pierwszym parametrem jest nazwa wyniku char[] variable. Drugi parametr to nazwa pliku file. Dane wyjściowe to C include filez zawartością pliku zakodowaną (małymi literami hex) jako podaną nazwę zmiennej. char arrayJest zero terminatedi długość danych są przechowywane w$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")';'

TUTAJ MOŻESZ OTRZYMAĆ XTR xtr (znak eXTRapolator) to GPLV3


1

Ponownie zaimplementowałem xxd w pythonie3, naprawiając wszystkie niedogodności xxd:

  • Stała poprawność
  • typ danych długości łańcucha: int → size_t
  • Zakończenie zerowe (na wypadek, gdybyś chciał)
  • Kompatybilny ze stringami C: Drop unsigned w tablicy.
  • Mniejsze, czytelne wyjście, tak jak byś je zapisał: Printable ascii jest wyprowadzane tak jak jest; inne bajty są kodowane szesnastkowo.

Oto skrypt, przefiltrowany przez siebie, więc możesz zobaczyć, co robi:

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;

Użycie (to wyodrębnia skrypt):

#include <stdio.h>

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

int main()
{
    fwrite(pyxxd, 1, pyxxd_len, stdout);
}

1

Co może zadziałać, jeśli zrobisz coś takiego:

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

Oczywiście będziesz musiał uważać na to, co faktycznie znajduje się w pliku , upewniając się, że nie ma podwójnych cudzysłowów, że wszystkie odpowiednie znaki są wyłączone itp.

Dlatego może być łatwiej, jeśli po prostu załadujesz tekst z pliku w czasie wykonywania lub osadzisz tekst bezpośrednio w kodzie.

Gdybyś nadal chciał mieć tekst w innym pliku, mógłbyś go tam mieć, ale musiałby być tam przedstawiony jako ciąg. Użyłbyś kodu jak powyżej, ale bez podwójnych cudzysłowów. Na przykład:

plik.txt

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

main.cpp

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

Tak więc zasadniczo posiadanie ciągu znaków w stylu C lub C ++ w dołączanym pliku tekstowym. Dzięki temu kod byłby bardziej uporządkowany, ponieważ na początku pliku nie ma tak dużej ilości tekstu.


3
Niezły pomysł, ale to nie zadziała, albo wystąpi błąd, ponieważ literał zawiera nową linię lub część #include zostanie odczytana jako ciąg i nie zostanie wykonana, niech to szlag, jeśli to zrobisz i przeklęte, jeśli tego nie zrobisz ... .
Motti

1
@Motti: uzgodniony - jak napisano, składniowo niepoprawny C. Pomysł jest interesujący - preprocesor C jest logicznie oddzielną fazą - ale praktyka jest taka, że ​​nie rusza z ziemi, ponieważ każda linia w dołączonym pliku zakończyć odwrotnym ukośnikiem itp.
Jonathan Leffler

2
Humm. Wydaje mi się, że nie powinieneś potrzebować odwrotnego ukośnika, ponieważ większość kompilatorów będzie łączyć ze sobą
sąsiednie

rzecz z tą odpowiedzią jest taka ... gdyby to było takie proste, nie sądzę, żeby OP kiedykolwiek zadałby to pytanie! -1, ponieważ obecność tej odpowiedzi nieco zachęca ludzi do marnowania czasu na próbowanie czegoś, co nie działa. Myślę, że moglibyśmy usunąć głos przeciw, gdybyś zmienił „Co może zadziałać” na „Dla porównania, to nie działa”
Mark Ch

@JonathanLeffler Po uruchomieniu preprocesora powinien on być poprawny w C lub C ++ w zależności od tego, jak sformatowano plik.txt.
Daemin

0

Nawet jeśli można to zrobić w czasie kompilacji (nie sądzę, aby tak było w ogóle), tekst będzie prawdopodobnie wstępnie przetworzonym nagłówkiem, a nie dosłowną zawartością plików. Spodziewam się, że będziesz musiał załadować tekst z pliku w czasie wykonywania lub wykonać nieprzyjemną pracę typu „wytnij i wklej”.


0

Odpowiedź Hasturkuna przy użyciu opcji xxd -i jest doskonała. Jeśli chcesz włączyć proces konwersji (tekst -> plik dołączania szesnastkowego) bezpośrednio do swojej kompilacji, narzędzie / biblioteka hexdump.c niedawno dodało możliwość podobną do opcji xxd -i (nie daje ci pełnego nagłówka - potrzebujesz aby podać definicję tablicy char - ale ma tę zaletę, że pozwala wybrać nazwę tablicy char):

http://25thandclement.com/~william/projects/hexdump.c.html

Jego licencja jest dużo bardziej "standardowa" niż xxd i jest bardzo liberalna - przykład wykorzystania jej do osadzenia pliku init w programie można zobaczyć w plikach CMakeLists.txt i scheme.c tutaj:

https://github.com/starseeker/tinyscheme-cmake

Uwzględnianie wygenerowanych plików w drzewach źródłowych i pakietowanie narzędzi ma zalety i wady - sposób obsługi tego zależy od konkretnych celów i potrzeb projektu. hexdump.c otwiera opcję tworzenia pakietów dla tej aplikacji.


0

Myślę, że nie jest to możliwe z samym kompilatorem i preprocesorem. gcc pozwala na to:

#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" );

Ale niestety nie to:

#define _STRGF(x) # x
#define STRGF(x) _STRGF(x)

    printk ( MODULE_NAME " built " __DATE__ " at " __TIME__ " on host "
            STRGF(
#               include "/etc/hostname"
            )
            "\n" );

Błąd:

/etc/hostname: In function ‘init_module’:
/etc/hostname:1:0: error: unterminated argument list invoking macro "STRGF"

Spojrzałem, jak mi każesz. Nie widzę żadnych nowych informacji w Twojej odpowiedzi (informacje, których nie ma w innych odpowiedziach), poza odniesieniem do /etc/hostnamesposobu osadzenia nazwy maszyny budującej w ciągu, który (nawet gdyby działał) nie byłby przenośny, ponieważ Mac OS X nie ma pliku /etc/hostname. Zauważ, że używanie nazw makr zaczynających się od podkreślenia, po którym następuje duża litera, oznacza używanie nazwy zarezerwowanej dla implementacji, czyli A Bad Thing ™.
Jonathan Leffler

0

Dlaczego nie połączyć tekstu z programem i użyć go jako zmiennej globalnej! Oto przykład. Rozważam użycie tego do włączenia plików cieniowania Open GL do pliku wykonywalnego, ponieważ shadery GL muszą być kompilowane dla GPU w czasie wykonywania.


0

Miałem podobne problemy, a dla małych plików wspomniane rozwiązanie Johannesa Schauba działało dla mnie jak urok.

Jednak w przypadku plików, które są nieco większe, napotkał problemy z limitem tablicy znaków kompilatora. Dlatego napisałem małą aplikację kodującą, która konwertuje zawartość pliku na tablicę znaków 2D o równej wielkości (i ewentualnie z zerami). Tworzy wyjściowe pliki tekstowe z danymi tablicowymi 2D, takimi jak:

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

gdzie 4 to w rzeczywistości zmienna MAX_CHARS_PER_ARRAY w koderze. Plik z wynikowym kodem C, nazywany na przykład „main_js_file_data.h”, można następnie łatwo wstawić do aplikacji C ++, na przykład w następujący sposób:

#include "main_js_file_data.h"

Oto kod źródłowy kodera:

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

0

Ten problem mnie irytował, a xxd nie działa w moim przypadku użycia, ponieważ spowodował, że zmienna o nazwie coś takiego jak __home_myname_build_prog_cmakelists_src_autogen, gdy próbowałem to skrypty, stworzyłem narzędzie do rozwiązania tego dokładnego problemu:

https://github.com/Exaeta/brcc

Generuje plik źródłowy i nagłówkowy oraz umożliwia jawne ustawienie nazwy każdej zmiennej, dzięki czemu można ich używać za pośrednictwem std :: begin (nazwa tablicy) i std :: end (nazwa tablicy).

Włączyłem to do mojego projektu cmake w następujący sposób:

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)

Przypuszczam, że dzięki drobnym poprawkom może działać również dla C.


0

Jeśli używasz CMake, prawdopodobnie możesz być zainteresowany napisaniem CMakeskryptu przetwarzania wstępnego, takiego jak poniżej:

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()

Twój CMakeLists.txt

include(cmake/ConvertLayout.cmake)
convert_layout_directory(layout ${CMAKE_BINARY_DIR}/include)
include_directories(${CMAKE_BINARY_DIR}/include)

gdzieś w c ++

#include "layout/menu.h"
Glib::ustring ui_info = LAYOUT_MENU;

-1

w xh

"this is a "
"buncha text"

w main.c

#include <stdio.h>
int main(void)
{
    char *textFileContents =
#include "x.h"
    ;

    printf("%s\n", textFileContents);

    return 0
}

powinien wykonać swoją pracę.


W przypadku wielu linii musisz dodać \ n więc: "linia 1 \ n" "linia 2 \ n"
Superfly Jon

jest to trochę mylące, oczywiście wymaga to pewnego przygotowania pliku tekstowego w celu dodania cudzysłowów i \ n znaków, nie działa w ogólnym przypadku
Mark Ch
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.