"#Include" eine Textdatei in einem C-Programm als char []

130

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

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

ein kleines Programm erhalten, das auf stdout druckt "Dies ist eine kleine Textdatei"

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 einen anderen Weg nennen, dies zu tun?

Brian Tompsett - 汤 莱恩
quelle
Schauen Sie hier nach, um eine Datei in ein Zeichen [] einzulesen. /programming/410943/reading-a-text-file-into-an-array-in-c Hier einige Tipps zur Verwendung der Makros des C-Präprozessors. http://gcc.gnu.org/onlinedocs/cpp/Macros.html
Daniel A. White
3
Warum willst du das tun? Warum nicht die Datei zur Laufzeit lesen? (Antwort: Vielleicht, weil es schwierig ist zu wissen, wo sich die Datei zur Laufzeit befindet, oder weil möglicherweise nur eine Datei installiert werden sollte.)
Jonathan Leffler
oder möglicherweise ist die Textdatei nur zur Kompilierungszeit verfügbar, z. B. Quellcode.
TMS
1
Manchmal möchten Sie zur Entwicklungszeit als separate Dateien auf Daten zugreifen, den Inhalt jedoch in Ihre Binärdatei kompilieren lassen. Beispiel ist das Ausführen eines Webservers auf einem Arduino, der keinen Zugriff auf lokalen Speicher hat. Sie möchten Ihre HTML-Dateien getrennt halten, um sie zu bearbeiten, aber beim Kompilieren müssen sie als Zeichenfolgen in Ihrer Quelle vorhanden sein.
Geordie

Antworten:

134

Ich würde vorschlagen, dafür (unix util) xxd zu verwenden. Sie können 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;
Hasturkun
quelle
18
Nur eine Anmerkung: Das von xxd erstellte Zeichen [] 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"};
2
Ich wusste nie über xxd. Es ist toll!
1
@eSKay: Das kommt direkt von der Ausgabe von xxd, wie die Antwort sagt. Der Name des Arrays ist der Eingabedateiname. Wenn Sie Daten einleiten, anstatt eine Eingabedatei zu verwenden, erhalten Sie stattdessen eine Liste mit hexadezimalen Werten (ohne die Array-Deklaration oder die Variable len).
Hasturkun
4
Dies ist äußerst nützlich beim Einbetten von GLSL-Shadern.
Linello
5
Ein anderer Weg, um xxd Terminierung 0x00 Terminierung hinzuzufügen, produzierte C-Code:xxd -i file.txt | sed 's/\([0-9a-f]\)$/\0, 0x00/' > file.h
vleo
103

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 geschehen :

Führen Sie in C ++ Folgendes aus:

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 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)====="
Kayahr
quelle
5
Vielen Dank, 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 in seine eigenen Dateien zu trennen und sie mit entsprechender Syntaxprüfung, Hervorhebung usw. zu bearbeiten.
YitzikC
1
Das ist wirklich nah an dem, was ich will. Insbesondere das benutzerdefinierte Trennzeichen. Sehr hilfreich. Ich möchte noch einen Schritt weiter gehen: Gibt es eine Möglichkeit, das Präfix R "(und das Suffix)" vollständig aus der Datei zu entfernen, die Sie einschließen möchten? Ich habe versucht, zwei Dateien mit den Namen bra.in und ket.in mit dem Präfix und dem Suffix zu definieren, einschließlich bra.in, file.txt und ket.in nacheinander. Aber der Compiler wertet den Inhalt von bra.in (das ist nur R "() aus, bevor er die nächste Datei einschließt. Es wird sich also beschweren. Bitte lassen Sie mich wissen, wenn jemand weiß, wie man Präfix und Suffix aus file.txt herausholt. Danke.
TMS
Ich vermute, C ++ würde R "(<newline> #include ...)" nicht zulassen? Es wäre schön, wenn die Datei zur Kompilierungszeit aufgenommen würde, um keinerlei Codierung zu erfordern ... dh gerade json oder xml oder csv oder was nicht ..
Brian Chrisman
Sie können den Text des Rohliteral etwas lesbarer machen, wenn Sie 1+R"...stattdessen R"...als Starttrennzeichen verwenden und dann vorher eine neue Zeile voranstellen Line 1. Dadurch wird der Ausdruck von einem Array in einen Zeiger umgewandelt. Dies ist hier jedoch kein wirkliches Problem, da Sie einen Zeiger und kein Array initialisieren.
Ruslan
14

Sie haben zwei Möglichkeiten:

  1. Verwenden Sie Compiler / Linker-Erweiterungen, um eine Datei in eine Binärdatei zu konvertieren. Die richtigen Symbole zeigen auf den Anfang und das Ende der Binärdaten. Siehe diese Antwort: Fügen Sie eine Binärdatei in das GNU ld-Linkerskript ein .
  2. Konvertieren Sie Ihre Datei in eine Folge von Zeichenkonstanten, mit denen ein Array initialisiert werden kann. Beachten Sie, dass Sie nicht einfach "" tun und mehrere Zeilen überspannen können. Sie benötigen ein Zeilenfortsetzungszeichen ( \), Escapezeichen "und andere, damit dies funktioniert. Einfacher, ein kleines Programm zu schreiben, um die Bytes in eine Sequenz wie zu konvertieren '\xFF', '\xAB', ...., '\0'(oder das xxddurch eine andere Antwort beschriebene Unix-Tool zu verwenden , falls verfügbar)!

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
Johannes Schaub - litb
quelle
1
Die letzte Zeile sollte wahrscheinlich "cat file.bin | ./bin2c> data.h" oder "./bin2c <file.bin> data.h"
lauten
Ich habe codeproject.com/Tips/845393/… verwendet , um eine Hex-Datei (unter Windows) aus einer Binärdatei zu erstellen, und dann Ihren Vorschlag von char my_file[] = { #include my_large_file.h };Thanks!
Jemand irgendwo
bin2cist nicht das gleiche bin2c wie von debian hxtools, Vorsicht
ThorSummoner
oder wenn ja, ist die Anrufung jetzt viel seltsamer:bin2c -H myoutput.h myinput1.txt myinputN.txt
ThorSummoner
9

ok, inspiriert von Daemins Beitrag habe ich folgendes einfaches 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 in Anführungszeichen.

Ilya
quelle
Darauf habe ich im letzten Teil meiner Antwort angespielt.
Daemin
Zitat, oder wie auch immer es heißt, verzeihen Sie mein Englisch
Ilya
Dies erfordert, dass die Daten C-maskiert sind. Ich glaube nicht, dass der Beitrag danach sucht. Wenn dies eine Art Include-Makro hätte, das dem Inhalt der Datei C-entkommen ist, wäre das in Ordnung.
Brian Chrisman
8

Ich mag Kayahrs Antwort. Wenn Sie die Eingabedateien jedoch nicht berühren möchten und CMake verwenden , können Sie der Datei die Begrenzungszeichenfolgen hinzufügen. Der folgende CMake-Code kopiert beispielsweise die Eingabedateien und verpackt 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)

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

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

Sie können dies tun mit 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 und die Symbole für den Anfang, das Ende und die Größe des Inhalts enthält myfile.txt.

John Zwinck
quelle
1
Können Sie uns die Symbolnamen mitteilen?
Mark Ch
@MarkCh: Gemäß den Dokumenten werden die Symbolnamen aus dem Eingabedateinamen generiert.
John Zwinck
Ich vermute, dass dies auf Nicht-x86-64-Computern nicht funktioniert, oder?
ThorSummoner
2

Du brauchst mein xtrDienstprogramm, aber du kannst es mit einem machen bash script. Dies ist ein Skript, das ich aufrufe bin2inc. Der erste Parameter ist der Name des Ergebnisses char[] variable. Der zweite Parameter ist der Name des file. Die Ausgabe ist C, include filewobei der Dateiinhalt (in Kleinbuchstaben hex) als Variablenname codiert ist . Das char arrayist zero terminatedund die Länge der Daten wird in gespeichert$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")';'

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


quelle
2

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

Angenommen, ich möchte einige SQL-Skripte für SQLite in mein Projekt aufnehmen und möchte Syntaxhervorhebungen erhalten, möchte aber keine spezielle Build-Infrastruktur. Ich kann diese Datei haben, test.sqldie SQL für SQLite ist, wo --ein Kommentar beginnt:

--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 einzufügen, test.pydie ein gültiges Python-Skript ist (weil #ein Kommentar in Python startet und passein No-Op ist):

#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 Streiche für verschiedene andere Codetypen zu spielen, die Sie möglicherweise als Zeichenfolge einfügen möchten. Ob es eine gute Idee ist oder nicht, weiß ich nicht genau. Es ist eine Art ordentlicher Hack, aber wahrscheinlich nichts, was Sie in echtem Produktionscode wollen würden. Könnte aber für ein Wochenend-Hack-Projekt in Ordnung sein.

mattnewport
quelle
Ich habe diesen Ansatz verwendet, um OpenGL-Shader auch in Textdateien einzufügen!
Yano
1

Ich habe xxd in python3 neu implementiert und alle Probleme von xxd behoben:

  • Konstante Korrektheit
  • Zeichenfolgendatentyp: int → size_t
  • Null-Kündigung (falls Sie das möchten)
  • C-String-kompatibel: unsignedAuf dem Array ablegen.
  • Kleinere, lesbare Ausgabe, wie Sie sie geschrieben hätten: Druckbare ASCII wird unverändert ausgegeben; 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 __name__ == '__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);
}
user2394284
quelle
1

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

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

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

Daher ist es möglicherweise einfacher, wenn Sie den Text zur Laufzeit nur aus einer Datei laden oder ihn 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 die doppelten 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 am Anfang der Datei nicht so viel Text vorhanden ist.

Daemin
quelle
3
Gute Idee, aber es wird nicht funktionieren. Entweder haben Sie einen Fehler, weil das Literal eine neue Zeile enthält, oder der Teil #include wird als Zeichenfolge gelesen und nicht ausgeführt. Verdammt, wenn Sie dies tun, und verdammt, wenn Sie es nicht tun. .
Motti
1
@Motti: vereinbart - wie geschrieben, syntaktisch ungültig C. Die Idee ist interessant - der C-Vorprozessor ist logischerweise eine separate Phase - aber die Praxis ist, dass er nicht in Gang kommt, weil jede Zeile in der enthaltenen Datei haben würde mit einem Backslash usw. zu beenden
Jonathan Leffler
2
Humm. Mir scheint, dass Sie den Backslash nicht benötigen sollten, da die meisten Compiler benachbarte Strings miteinander verketten
EvilTeach
Die Sache mit dieser Antwort ist ... wenn es so einfach wäre, hätte das OP die Frage wohl nie gestellt! -1, weil das Vorhandensein dieser Antwort die Leute leicht ermutigt, ihre Zeit damit zu verschwenden, etwas auszuprobieren, das nicht funktioniert. Ich denke, wir könnten die Abstimmung entfernen, wenn Sie "Was könnte funktionieren" in "Als Referenz funktioniert dies nicht" ändern
Mark Ch
@JonathanLeffler Nachdem der Präprozessor ausgeführt wurde, sollte er je nach Formatierung von file.txt in C oder C ++ gültig sein.
Daemin
0

Selbst wenn dies zur Kompilierungszeit möglich ist (ich glaube nicht, dass dies im Allgemeinen möglich ist), wäre der Text wahrscheinlich eher der vorverarbeitete Header als der wörtliche Inhalt der Dateien. 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.

Daniel Paull
quelle
0

Hasturkuns Antwort mit der Option xxd -i ist ausgezeichnet. Wenn Sie den Konvertierungsprozess (Text -> Hex-Include-Datei) direkt in Ihren 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 erhalten nicht den vollständigen Header -, die Sie benötigen um die Definition des char-Arrays 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 "Standard" als xxd und sehr liberal. Ein Beispiel für die Verwendung zum Einbetten einer Init-Datei in ein Programm finden Sie in den Dateien CMakeLists.txt und Schema.c hier:

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

Das Einfügen generierter Dateien in Quellbäume und das Bündeln von Dienstprogrammen hat 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.

Sternensucher
quelle
0

Ich denke, dass es mit dem Compiler und Präprozessor allein nicht 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"
kein Benutzer
quelle
Ich habe geschaut, wie du mir geboten hast zu schauen. Ich sehe keine neuen Informationen in Ihrer Antwort (Informationen, die nicht in anderen Antworten enthalten sind), außer einem Verweis auf /etc/hostnameeine Möglichkeit, den Namen der Build-Maschine in die Zeichenfolge einzubetten, was (selbst wenn es funktionieren würde) nicht wäre portabel, da Mac OS X keine Datei hat /etc/hostname. Beachten Sie, dass bei der Verwendung von Makronamen, die mit einem Unterstrich gefolgt von einem Großbuchstaben beginnen, ein für die Implementierung reservierter Name verwendet wird, nämlich A Bad Thing ™.
Jonathan Leffler
0

Verknüpfen Sie den Text mit dem Programm und verwenden Sie ihn als globale Variable. Hier ist ein Beispiel. Ich denke darüber nach, Open GL-Shader-Dateien in eine ausführbare Datei aufzunehmen, da GL-Shader zur Laufzeit für die GPU kompiliert werden müssen.

TechDragon
quelle
0

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 etwas größeren Dateien traten jedoch Probleme mit dem Zeichenarray-Limit des Compilers auf. Aus diesem Grund habe ich eine kleine Encoder-Anwendung geschrieben, die Dateiinhalte in ein 2D-Zeichenarray mit gleich großen Blöcken (und möglicherweise aufgefüllten Nullen) konvertiert. Es werden Ausgabetextdateien mit 2D-Array-Daten wie folgt erstellt:

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, beispielsweise "main_js_file_data.h", kann dann einfach in die C ++ - Anwendung eingefügt werden, beispielsweise wie folgt:

#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;
}
Volzotan
quelle
0

Dieses Problem hat mich irritiert und xxd funktioniert für meinen Anwendungsfall nicht, da die Variable __home_myname_build_prog_cmakelists_src_autogen beim Versuch, ein Skript zu erstellen, erstellt wurde. Daher habe ich ein Dienstprogramm erstellt, um genau dieses Problem zu lösen:

https://github.com/Exaeta/brcc

Es generiert eine Quell- und Header-Datei und ermöglicht es Ihnen, den Namen jeder Variablen explizit festzulegen, damit Sie sie über std :: begin (arrayname) und std :: end (arrayname) verwenden können.

Ich habe es wie folgt in mein cmake-Projekt aufgenommen:

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)

Mit kleinen Änderungen könnte es auch für C funktionieren.

Exaeta
quelle
-1

in xh

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

EvilTeach
quelle
Für mehrere Zeilen müssen Sie \ n hinzufügen, also: "Zeile 1 \ n" "Zeile 2 \ n"
Superfly Jon
Es ist ein bisschen irreführend, offensichtlich erfordert dies eine gewisse Vorbereitung der Textdatei, um Anführungszeichen und \ n Zeichen hinzuzufügen. Funktioniert im allgemeinen Fall nicht
Mark Ch