it-swarm.com.de

"#include" eine Textdatei in einem C-Programm als Zeichen []

Gibt es eine Möglichkeit, eine gesamte Textdatei zur Kompilierungszeit als Zeichenfolge in ein C-Programm aufzunehmen?

so etwas wie:

  • file.txt:

    This is
    a little
    text file
    
  • haupt c:

    #include <stdio.h>
    int main(void) {
       #blackmagicinclude("file.txt", content)
       /*
       equiv: char[] content = "This is\na little\ntext file";
       */
       printf("%s", content);
    }
    

erhalten eines kleinen Programms, das auf stdout gedruckt wird "Dies ist eine kleine Textdatei"

Im Moment habe ich ein hackisches python Skript verwendet, aber es ist stumpf und auf nur einen Variablennamen beschränkt. Kannst du mir einen anderen Weg dazu nennen?

120
ZeD

Ich würde vorschlagen, dafür (unix util) xxd zu verwenden. du kannst es so benutzen

$ echo hello world > a
$ xxd -i a

ausgänge:

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

Die Frage betraf C, aber falls jemand versucht, dies mit C++ 11 zu tun, kann dies dank der neuen rohen String-Literale mit nur geringen Änderungen an der enthaltenen Textdatei durchgeführt werden:

In C++ machen Sie folgendes:

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

Führen Sie in der Textdatei Folgendes aus:

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

Es darf also nur ein Präfix am Anfang der Datei und ein Suffix am Ende der Datei stehen. Dazwischen können Sie tun, was Sie wollen, es ist kein spezielles Escape erforderlich, solange Sie die Zeichenfolge )" Nicht benötigen. Aber auch dies kann funktionieren, wenn Sie einen eigenen benutzerdefinierten Begrenzer angeben:

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

Sie haben zwei Möglichkeiten:

  1. Verwenden Sie Compiler/Linker-Erweiterungen, um eine Datei in eine Binärdatei zu konvertieren, wobei die richtigen Symbole auf den Anfang und das Ende der Binärdaten verweisen. Siehe diese Antwort: Include binary file with GNU ld linker script .
  2. Konvertieren Sie Ihre Datei in eine Folge von Zeichenkonstanten, die ein Array initialisieren können. Beachten Sie, dass Sie nicht einfach mehrere Zeilen "" machen und überspannen können. Sie benötigen ein Zeilenfortsetzungszeichen (\), Flucht " Zeichen und andere, damit das funktioniert. Einfacheres Schreiben eines kleinen Programms zum Konvertieren der Bytes in eine Sequenz wie '\xFF', '\xAB', ...., '\0' (oder verwenden Sie das von einer anderen Antwort beschriebene Unix-Tool xxd, wenn Sie es zur Verfügung haben!):

Code:

#include <stdio.h>

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

(nicht getestet). Dann mach:

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

Wobei data.h von generiert wird

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

ok, inspiriert von Daemins Post habe ich das folgende einfache Beispiel getestet:

a.data:

"this is test\n file\n"

test.c:

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

gcc -E test.c Ausgabe:

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

Es funktioniert also, erfordert jedoch Daten, die in Anführungszeichen gesetzt sind.

8
Ilya

Ich mag Kayahrs Antwort. Wenn Sie die Eingabedateien nicht berühren möchten Wenn Sie jedoch CMake verwenden, können Sie der Datei die Begrenzungszeichenfolgen hinzufügen. Der folgende CMake-Code kopiert beispielsweise die Eingabedateien und bricht ihren Inhalt entsprechend um:

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)

Fügen Sie dann in c ++ Folgendes ein:

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

Sie können dies mit objcopy tun:

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

Jetzt haben Sie eine Objektdatei, die Sie in Ihre ausführbare Datei verlinken können und die Symbole für Anfang, Ende und Größe des Inhalts aus myfile.txt Enthält.

2
John Zwinck

Wenn Sie gewillt sind, auf schmutzige Tricks zurückzugreifen, können Sie mit unformatierten String-Literalen und #include Für bestimmte Dateitypen kreativ werden.

Angenommen, ich möchte einige SQL-Skripte für SQLite in mein Projekt aufnehmen und möchte Syntax-Hervorhebungen erhalten, möchte aber keine spezielle Build-Infrastruktur. Ich kann diese Datei test.sql Haben, die für SQLite gültig ist, wobei -- Einen Kommentar startet:

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

Und dann kann ich in meinem C++ - Code Folgendes haben:

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

    cout << mysql << endl;
}

Die Ausgabe ist:

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

Oder um Python Code aus einer Datei test.py Einzufügen, die ein gültiges Python Skript ist (weil # Einen Kommentar startet) in Python und pass ist ein No-Op):

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

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

Und dann im C++ Code:

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

    cout << mypython << endl;
}

Welches wird ausgegeben:

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

myfunc()
#undef pass
#define pass

Es sollte möglich sein, ähnliche Tricks für verschiedene andere Codetypen zu spielen, die Sie möglicherweise als Zeichenfolge verwenden möchten. Ob es eine gute Idee ist oder nicht, weiß ich nicht. Es ist eine Art ordentlicher Hack, aber wahrscheinlich nicht das, was man sich in echtem Produktionscode wünscht. Könnte aber für ein Wochenend-Hack-Projekt in Ordnung sein.

2
mattnewport

Was könnte funktionieren, wenn Sie etwas tun wie:

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

Natürlich müssen Sie achten Sie darauf, was tatsächlich in der Datei enthalten ist sicherstellen, dass keine doppelten Anführungszeichen vorhanden sind, dass alle entsprechenden Zeichen maskiert sind usw.

Daher ist es möglicherweise einfacher, wenn Sie nur den Text zur Laufzeit aus einer Datei laden oder den Text direkt in den Code einbetten.

Wenn Sie den Text noch in einer anderen Datei haben möchten, könnten Sie ihn dort haben, aber er müsste dort als Zeichenfolge dargestellt werden. Sie würden den Code wie oben verwenden, jedoch ohne doppelte Anführungszeichen. Beispielsweise:

file.txt

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

main.cpp

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

Im Grunde genommen haben Sie eine Zeichenfolge im C- oder C++ - Stil in einer Textdatei, die Sie einschließen. Dies würde den Code übersichtlicher machen, da sich am Anfang der Datei nicht so viel Text befindet.

2
Daemin

Sie benötigen mein Dienstprogramm xtr, können es jedoch mit einem bash script Ausführen. Dies ist ein Skript, das ich bin2inc Nenne. Der erste Parameter ist der Name des resultierenden char[] variable. Der zweite Parameter ist der Name des file. Die Ausgabe ist C include file, Wobei der Dateiinhalt (in Kleinbuchstaben hex) als Variablenname angegeben wird. Der char array Ist zero terminated Und die Länge der Daten ist in $variableName_length Gespeichert.

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

SIE KÖNNEN XTR HIER ERHALTEN xtr (character eXTRapolator) ist GPLV3

2
user735796

Ich habe xxd in python3 reimplementiert und alle Ärgernisse von xxd behoben:

  • Const Richtigkeit
  • stringlänge Datentyp: int → size_t
  • Nullterminierung (für den Fall, dass Sie das möchten)
  • Kompatibel mit C-Strings: Fügen Sie unsigned in das Array ein.
  • Kleinere, lesbare Ausgabe, wie Sie sie geschrieben hätten: Druckbare ASCII-Dateien werden so ausgegeben, wie sie sind. andere Bytes sind hexadezimal codiert.

Hier ist das Skript, das von selbst gefiltert wurde, damit Sie sehen können, was es tut:

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 __== '__main__':\n"
"    main()\n"
"";

const size_t pyxxd_len = sizeof(pyxxd) - 1;

Verwendung (dies extrahiert das Skript):

#include <stdio.h>

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

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

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

sollte den Job machen.

0
EvilTeach

Selbst wenn dies zur Kompilierungszeit möglich ist (ich glaube nicht, dass dies im Allgemeinen möglich ist), handelt es sich bei dem Text wahrscheinlich um den vorverarbeiteten Header und nicht um den Inhalt der Dateien im Wortlaut. Ich gehe davon aus, dass Sie den Text zur Laufzeit aus der Datei laden oder einen fiesen Job zum Ausschneiden und Einfügen ausführen müssen.

0
Daniel Paull

Ich denke, dass es nicht nur mit dem Compiler und dem Präprozessor möglich ist. gcc erlaubt dies:

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

Aber leider nicht das:

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

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

Der Fehler ist:

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

Hasturkuns Antwort mit der Option xxd -i ist ausgezeichnet. Wenn Sie den Konvertierungsprozess (Text -> Hex-Include-Datei) direkt in Ihr Build integrieren möchten, hat das Tool/die Bibliothek hexdump.c kürzlich eine Funktion hinzugefügt, die der Option -i von xxd ähnelt (sie bietet nicht den vollständigen Header - den Sie benötigen) um die char-Array-Definition bereitzustellen - dies hat jedoch den Vorteil, dass Sie den Namen des char-Arrays auswählen können):

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

Die Lizenz ist viel mehr "Standard" als xxd und ist sehr liberal - ein Beispiel für die Verwendung zum Einbetten einer Init-Datei in ein Programm finden Sie in den Dateien CMakeLists.txt und scheme.c hier:

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

Das Einbeziehen generierter Dateien in die Quellbäume und das Bündeln von Dienstprogrammen haben Vor- und Nachteile. Wie Sie damit umgehen, hängt von den spezifischen Zielen und Anforderungen Ihres Projekts ab. hexdump.c öffnet die Bündelungsoption für diese Anwendung.

0
starseeker

Ich hatte ähnliche Probleme, und für kleine Dateien wirkte die oben erwähnte Lösung von Johannes Schaub wie ein Zauber für mich.

Bei Dateien, die etwas größer sind, kam es jedoch zu Problemen mit der Zeichenfeldbegrenzung des Compilers. Aus diesem Grund habe ich eine kleine Encoder-Anwendung geschrieben, die den Dateiinhalt in ein 2D-Zeichenarray mit gleichgroßen Blöcken (und möglicherweise Auffüllen von Nullen) konvertiert. Es erzeugt Ausgabe-Textdateien mit 2D-Array-Daten wie folgt:

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

dabei ist 4 tatsächlich eine Variable MAX_CHARS_PER_ARRAY im Encoder. Die Datei mit dem resultierenden C-Code, zum Beispiel "main_js_file_data.h" genannt, kann dann einfach in die C++ - Anwendung eingebunden werden, zum Beispiel so:

#include "main_js_file_data.h"

Hier ist der Quellcode des Encoders:

#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
volzotan

Warum verknüpfen Sie den Text nicht mit dem Programm und verwenden ihn als globale Variable? Hier ist ein Beispiel. Ich erwäge, dies zu verwenden, um Open GL Shader-Dateien in eine ausführbare Datei aufzunehmen, da GL Shader müssen zur Laufzeit für die GPU kompiliert werden.

0
TechDragon