"#include " plik tekstowy w programie C jako znak[]

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

Coś w stylu:

  • 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 trochę plik tekstowy "

W tej chwili użyłem hackish python script, ale jest butt-brzydki i ograniczony tylko do jednej nazwy zmiennej, możesz mi powiedzieć inny sposób, aby to zrobić?

Author: Brian Tompsett - 汤莱恩, 2009-01-04

15 answers

Sugerowałbym użycie (unix util) xxd do tego celu. 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;
 108
Author: Hasturkun,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-01-04 13:56:22

Pytanie dotyczyło C, ale w przypadku, gdy ktoś spróbuje zrobić to z C++11, można to zrobić tylko z niewielkimi zmianami w dołączonym pliku tekstowym dzięki nowym surowym literałom łańcuchowym :

W C++ zrób to:

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

W pliku tekstowym zrób tak:

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

Więc musi być tylko prefiks na górze pliku i sufiks na jego końcu. Pomiędzy nimi możesz robić, co chcesz, żadne specjalne ucieczki nie są konieczne, o ile nie potrzebujesz sekwencji znaków )". Ale nawet to może działać, jeśli podasz swój własny ogranicznik:

R"=====(Line 1
Line 2
Line 3
Now you can use "( and )" in the text file, too.
Line 5
Line 6)====="
 82
Author: kayahr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-07 18:14:34

Masz dwie możliwości:

  1. Użyj rozszerzeń kompilatora/linkera, aby przekonwertować plik do pliku binarnego, z odpowiednimi symbolami wskazującymi początek i koniec danych binarnych. Zobacz tę odpowiedź: Dołącz plik binarny za pomocą skryptu linkera GNU LD .
  2. Konwertuj plik do sekwencji stałych znaków, które mogą zainicjalizować tablicę. Uwaga nie można po prostu zrobić "" i rozciągnąć wiele linii. Potrzebujesz znaku kontynuacji linii (\), escape " postaci i innych, aby to zadziałało. Łatwiej jest napisać mały program do konwersji bajtów na sekwencję podobną do '\xFF', '\xAB', ...., '\0' (lub użyć narzędzia uniksowego xxd opisanego przez inną odpowiedź, jeśli masz ją dostępną!):

Kod:

#include <stdio.h>

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

(nie testowane). Więc zrób:

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

Gdzie dane.H jest generowane przez

cat file.bin | ./bin2c > data.h
 14
Author: Johannes Schaub - litb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:55:03

OK, zainspirowany postem Daemin ' a przetestowałem następujący prosty przykład:

A. data:

"this is test\n file\n"

Test.c:

int main(void)
{
    char *test = 
#include "a.data"
    ;
    return 0;
}
Test Gcc - E.wyjście 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 otoczonych cudzysłowami.

 8
Author: Ilya,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:47:32

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 jest w pliku, upewniając się, że nie ma podwójnych cudzysłowów, że wszystkie odpowiednie znaki są unikalne itp.

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

Jeśli nadal chcesz tekst w innym pliku, możesz go tam mieć, ale musiałby być tam reprezentowany jako sznurek. Możesz użyć kodu jak powyżej, ale bez podwójnych cudzysłowów w nim. Na przykład:

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

int main()
{
    const char* text =
#include "file.txt"
;
    printf("%s", text);
    return 0;
}
 4
Author: Daemin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-01-04 22:15:22

Potrzebujesz mojego xtr narzędzia, ale możesz to zrobić za pomocą bash script. To jest skrypt, który nazywam bin2inc. Pierwszym parametrem jest nazwa wynikowego char[] variable. Drugim parametrem jest nazwa file. Wyjście to C {[6] } z zawartością pliku zakodowaną (małymi literami hex) jako podana nazwa zmiennej. char array jest zero terminated, a długość danych jest przechowywana 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")';'

Możesz pobrać XTR tutaj xtr (ekstrapolator znaków) to GPLV3

 2
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-12-10 04:16:20

Możesz to zrobić używając 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 dla początku, końca i rozmiaru zawartości z myfile.txt.

 1
Author: John Zwinck,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-14 14:37:24

Podoba mi się odpowiedź kayahr. Jeśli jednak nie chcesz dotykać plików wejściowych , a jeśli używasz CMake , możesz dodać sekwencje znaków delimetra do pliku. Na przykład następujący kod CMake kopiuje pliki wejściowe i odpowiednio zawija 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++ Tak:

constexpr char *test =
#include "generated/cool.frag"
;
 1
Author: Martin R.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-13 23:23:47

I reimplemented xxd in python3, fixing all of xxd ' s annoyances:

  • Constitution
  • string length datatype: int → size_t
  • null termination (In case you might want that)
  • C string compatible: Drop unsigned na tablicy.
  • Mniejsze, czytelne Wyjście, tak jak byś to napisał: Printable ascii jest wyjściem as-is; inne bajty są zakodowane szesnastkowo.

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

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 wyciąga skrypt):

#include <stdio.h>

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

int main()
{
    fwrite(pyxxd, 1, pyxxd_len, stdout);
}
 1
Author: user2394284,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-27 11:29:51

Nawet jeśli można to zrobić w czasie kompilacji (nie sądzę, że w ogóle), tekst prawdopodobnie będzie wstępnie przetworzonym nagłówkiem, a nie zawartością plików dosłownie. Spodziewam się, że będziesz musiał załadować tekst z pliku w czasie wykonywania lub wykonać paskudne zadanie cut-n-paste.

 0
Author: Daniel Paull,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-01-04 13:54:40

W x.h

"this is a "
"buncha text"

In main.c

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

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

    return 0
}
/ Align = "left" /
 0
Author: EvilTeach,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-01-04 21:58:26

Odpowiedź Hasturkuna za pomocą opcji xxd-i jest doskonała. Jeśli chcesz włączyć proces konwersji (tekst - > hex Dołącz plik) bezpośrednio do kompilacji zrzutu heksowego.narzędzie / biblioteka c ostatnio dodała możliwość podobną do opcji xxd-i (nie daje pełnego nagłówka-musisz podać definicję tablicy znaków - ale to ma tę zaletę, że pozwala wybrać nazwę znaku array):

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

Jego licencja jest o wiele bardziej "standardowa" niż xxd i jest bardzo liberalna - przykład użycia jej do osadzenia pliku init w programie można zobaczyć w Cmakelistach.txt i scheme.pliki c Tutaj:

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

Istnieją plusy i minusy zarówno w tym generowanych plików w drzewach źródłowych, jak i wiązania narzędzi-jak sobie z tym poradzić będzie zależeć od konkretne cele i potrzeby Twojego projektu. zrzut heksowy.c otwiera opcję wiązania dla tej aplikacji.

 0
Author: starseeker,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-04-14 04:20:10

Myślę, że nie jest to możliwe tylko z 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 to:

/etc/hostname: In function ‘init_module’:
/etc/hostname:1:0: error: unterminated argument list invoking macro "STRGF"
 0
Author: not-a-user,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-15 13:59:29

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 otwartych plików GL shader w pliku wykonywalnym, ponieważ shadery GL muszą być skompilowane dla GPU w czasie wykonywania.

 0
Author: TechDragon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-11 05:24:18

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

Jednak w przypadku plików, które są nieco większe, napotkano problemy z limitem tablicy znaków kompilatora. Dlatego napisałem małą aplikację kodującą, która konwertuje zawartość pliku do tablicy znaków 2D o jednakowych rozmiarach (i ewentualnie Zerach wypełniających). Generuje wyjściowe pliki tekstowe z danymi tablicy 2D w następujący sposób:

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 jest 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że być następnie łatwo zaimplementowany w 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
Author: volzotan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-31 00:03:01