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

Lesezeit: 9 Minuten

Gibt es eine Möglichkeit, eine vollständige Textdatei zur Kompilierzeit als Zeichenfolge in ein C-Programm einzufügen?

etwas wie:

  • Datei.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 “Dies ist eine kleine Textdatei” ausgibt

Im Moment habe ich ein hackiges Python-Skript verwendet, aber es ist hässlich und auf nur einen Variablennamen beschränkt. Können Sie mir eine andere Möglichkeit nennen?

  • Sehen Sie sich hier an, wie Sie eine Datei in ein Zeichen einlesen[]. https://stackoverflow.com/questions/410943/reading-a-text-file-into-an-array-in-c Hier sind einige Tipps zur Verwendung der Makros des C-Präprozessors. http://gcc.gnu.org/onlinedocs/cpp/Macros.html

    – Daniel A. Weiß

    4. Januar 2009 um 13:41 Uhr

  • Warum willst du das tun? Warum die Datei nicht zur Laufzeit lesen? (Antwort: Vielleicht, weil es zur Laufzeit schwer zu wissen ist, wo sich die Datei befindet, oder vielleicht, weil es nur eine Datei zum Installieren geben sollte.)

    – Jonathan Leffler

    4. Januar 2009 um 16:43 Uhr

  • oder vielleicht ist die Textdatei nur zur Kompilierzeit verfügbar, z. B. Quellcode.

    – TMS

    21. September 2018 um 15:11 Uhr

  • Manchmal möchten Sie zur Entwicklungszeit auf Daten als separate Dateien zugreifen, aber den Inhalt in Ihre Binärdatei kompilieren lassen. Ein Beispiel ist das Ausführen eines Webservers auf einem Arduino, der keinen Zugriff auf den lokalen Speicher hat. Sie möchten Ihre HTML-Dateien getrennt halten, um sie zu bearbeiten, aber zur Kompilierzeit müssen sie als Zeichenfolgen in Ihrer Quelle vorhanden sein.

    – Georgy

    27. Januar 2019 um 8:43 Uhr


  • C23 wird haben #embedwas so klingt, als wäre es dafür nützlich.

    – David Fong

    31. Juli um 5:16 Uhr


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

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

  • Nur eine Anmerkung: die char[] erstellt von xxd ist nicht NULL-terminiert! also mache ich $ xxd -i < file.txt > file.xxd $ echo ‘, 0’ >> file.xxd und in der main.c char file_content[] = { #include “file.xxd” };

    ZeD

    4. Januar 2009 um 16:10 Uhr

  • Ich wusste nie über xxd. Es ist toll!

    anon

    6. Januar 2009 um 0:29 Uhr

  • @eSKay: das kommt direkt aus der Ausgabe von xxd, wie die Antwort sagt. Der Name des Arrays ist der Eingabedateiname. wenn Sie Daten übergeben, anstatt eine Eingabedatei zu verwenden, erhalten Sie stattdessen eine Liste mit Hexadezimalwerten (ohne die Array-Deklaration oder die len-Variable).

    – Hasturkun

    21. März 2010 um 23:42 Uhr

  • Das ist äußerst nützlich beim Einbetten von GLSL-Shadern.

    – Linell

    18. Januar 2016 um 11:11 Uhr

  • Eine andere Möglichkeit, 0x00-Terminierung zu xxd-produziertem C-Code hinzuzufügen: xxd -i file.txt | sed 's/\([0-9a-f]\)$/\0, 0x00/' > file.h

    – vleo

    26. Februar 2016 um 17:36 Uhr


Benutzeravatar von kayahr
Kayahr

Die Frage bezog sich auf C, aber falls jemand versucht, es mit C ++ 11 zu tun, kann dies dank des neuen mit nur geringfügigen Änderungen an der enthaltenen Textdatei erfolgen rohe Zeichenfolgenliterale:

In C++ tun Sie dies:

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

In der Textdatei tun Sie dies:

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

Es muss also nur ein Präfix am Anfang der Datei und ein Suffix am Ende stehen. Dazwischen können Sie machen, was Sie wollen, es ist kein spezielles Escapezeichen nötig, solange Sie die Zeichenfolge nicht benötigen )". Aber auch das kann funktionieren, wenn Sie Ihr eigenes benutzerdefiniertes Trennzeichen angeben:

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

  • Danke, ich habe die hier vorgeschlagene Methode gewählt, um lange SQL-Fragmente in meinen C++ 11-Code einzubetten. Dies ermöglicht es mir, das SQL sauber in seine eigenen Dateien zu trennen und sie mit entsprechender Syntaxprüfung, Hervorhebung usw. zu bearbeiten.

    – Isac Casapu

    29. Januar 2017 um 21:58 Uhr


  • Das ist wirklich nah an dem, was ich will. Vor allem das benutzerdefinierte Trennzeichen. Sehr hilfreich. Ich möchte noch einen Schritt weiter gehen: Gibt es eine Möglichkeit, das Präfix R”( und Suffix )” vollständig aus der Datei zu entfernen, die Sie einschließen möchten? Ich habe versucht, zwei Dateien namens bra.in und ket.in mit dem Präfix und Suffix darin zu definieren, bra.in, file.txt und ket.in nacheinander einzuschließen. Aber der Compiler wertet den Inhalt von bra.in aus (was nur R “() ist, bevor er die nächste Datei einfügt. Also wird er sich beschweren. Bitte lassen Sie mich wissen, ob jemand weiß, wie man Präfix und Suffix aus file.txt herausbekommt. Danke.

    – TMS

    21. September 2018 um 4:16 Uhr

  • Ich vermute, C++ würde R”(#include…)” nicht zulassen? Wäre schön, wenn die Datei zur Kompilierungszeit aufgenommen würde, um keinerlei Codierung zu erfordern …. dh direkt json oder xml oder csv oder was nicht.

    – Brian Chrisman

    10. August 2019 um 16:09 Uhr


  • Sie können den Text des Rohliterals etwas lesbarer machen, wenn Sie verwenden 1+R"... als Starttrennzeichen statt R"...und stellen Sie dann einen Zeilenumbruch voran Line 1. Dadurch wird der Ausdruck von einem Array in einen Zeiger umgewandelt, aber das ist hier kein wirkliches Problem, da Sie einen Zeiger und kein Array initialisieren.

    – Ruslan

    1. Januar 2020 um 17:39 Uhr

  • @Brian Chrisman Das scheint in GCC gut zu funktionieren

    – 0xB00B

    27. Januar um 14:52 Uhr

Benutzeravatar von Martin R
Martin R.

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

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)

Dann in c++ wie folgt einfügen:

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

  • Groß. Danke+

    – Ilja Polishchuk

    4. Oktober 2020 um 13:48 Uhr

  • Ich liebe diese Antwort!

    – Allee

    1. Januar 2021 um 7:19 Uhr

  • Gute Antwort! Der einzige Nachteil ist, dass die Funktion nur (einmal) zur Cmake-Konfigurationszeit ausgewertet wird. Dies zur Build-Zeit zu tun und das Buildsystem die Eingabedatei auf Änderungen überwachen zu lassen, ist viel schwieriger als ich dachte …

    – Hut

    22. April um 9:43 Uhr

Johannes Schaub - Benutzerbild der litb
Johannes Schaub – litb

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 zeigen. Siehe diese Antwort: Binärdatei mit GNU-ld-Linker-Skript einschließen.
  2. Konvertieren Sie Ihre Datei in eine Folge von Zeichenkonstanten, die ein Array initialisieren können. Beachten Sie, dass Sie nicht einfach “” ausführen und sich über mehrere Zeilen erstrecken können. Sie benötigen ein Zeilenfortsetzungszeichen (\), Flucht " Charaktere und andere, damit das funktioniert. Einfacher ist es, einfach ein kleines Programm zu schreiben, um die Bytes in eine Sequenz wie z '\xFF', '\xAB', ...., '\0' (Oder verwenden Sie das Unix-Tool xxd beschrieben durch eine andere Antwort, 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"
};

Wo data.h generiert wird

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

Benutzeravatar von Ilya
Ilja

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

a.Daten:

"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 aber Daten, die in Anführungszeichen gesetzt sind.

  • Darauf wollte ich im letzten Teil meiner Antwort anspielen.

    – Dominik Grabiec

    4. Januar 2009 um 22:11 Uhr

  • Zitat, oder wie auch immer es heißt, verzeihen Sie mein Englisch

    – Ilja

    29. Juli 2009 um 15:32 Uhr

  • Dies erfordert, dass die Daten C-escaped werden. Ich glaube nicht, dass der Beitrag danach sucht. Wenn dies eine Art Include-Makro hätte, das den Inhalt der Datei C-escaped, wäre das in Ordnung.

    – Brian Chrisman

    10. August 2019 um 16:08 Uhr

Benutzeravatar von John Zwinck
Johannes Zwinck

Sie können dies mit tun objcopy:

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

Jetzt haben Sie eine Objektdatei, die Sie mit Ihrer ausführbaren Datei verknüpfen können, die Symbole für den Anfang, das Ende und die Größe des Inhalts enthält myfile.txt.

  • Darauf wollte ich im letzten Teil meiner Antwort anspielen.

    – Dominik Grabiec

    4. Januar 2009 um 22:11 Uhr

  • Zitat, oder wie auch immer es heißt, verzeihen Sie mein Englisch

    – Ilja

    29. Juli 2009 um 15:32 Uhr

  • Dies erfordert, dass die Daten C-escaped werden. Ich glaube nicht, dass der Beitrag danach sucht. Wenn dies eine Art Include-Makro hätte, das den Inhalt der Datei C-escaped, wäre das in Ordnung.

    – Brian Chrisman

    10. August 2019 um 16:08 Uhr

Benutzeravatar von mattnewport
mattnewport

Wenn Sie bereit sind, auf einige schmutzige Tricks zurückzugreifen, können Sie mit rohen Zeichenfolgenliteralen und kreativ werden #include für bestimmte Dateitypen.

Angenommen, ich möchte einige SQL-Skripts für SQLite in mein Projekt einbinden und Syntaxhervorhebung erhalten, möchte aber keine spezielle Build-Infrastruktur. Ich kann diese Datei haben test.sql das ist gültiges SQL für SQLite wo -- beginnt einen Kommentar:

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

Und dann kann ich in meinem C++-Code 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 einzufügen test.py was ein gültiges Python-Skript ist (weil # startet einen Kommentar 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;
}

Was wird ausgeben:

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

myfunc()
#undef pass
#define pass

Es sollte möglich sein, ähnliche Tricks für verschiedene andere Arten von Code zu spielen, die Sie vielleicht als Zeichenfolge einschließen möchten. Ob es eine gute Idee ist, bin ich mir nicht sicher. Es ist ein netter Hack, aber wahrscheinlich nichts, was Sie in echtem Produktionscode wollen würden. Könnte aber für ein Wochenend-Hack-Projekt in Ordnung sein.

  • Ich habe diesen Ansatz verwendet, um OpenGL-Shader auch in Textdateien einzufügen!

    – jano

    5. August 2019 um 16:51 Uhr

  • Funktioniert super! Ich benutze es, um kleine LUA-Skripte einzubinden, die den Kommentar mit “–” beginnen.

    – yichun

    11. Oktober 2021 um 3:08 Uhr

1424630cookie-check„#include“ eine Textdatei in einem C-Programm als Zeichen[]

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy