Frage

Gibt es eine Möglichkeit, eine ganze Textdatei als String in einem C-Programm zur Compile-Zeit enthalten?

so etwas wie:

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

ein kleines Programm zu erhalten, die auf stdout druckt „Das ist ein wenig Textdatei "

Im Moment habe ich ein hackish Python-Skript, aber es ist stumpf hässlich und beschränkt sich auf nur einen Variablennamen, können Sie mir eine andere Art und Weise sagen, es zu tun?

War es hilfreich?

Lösung

Ich würde vorschlagen, mit (Unix-util) xxd für diese. Sie können es wie 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;

Andere Tipps

Die Frage war etwa C, aber falls jemand versucht, es zu tun mit C ++ 11, dann kann es mit nur wenig Änderungen an der mitgelieferten Textdatei dank den neuen raw Stringliterale :

In C ++ tun:

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

In der Textdatei dies tun:

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

Es muss also nur ein Präfix sein am Anfang der Datei und ein Suffix am Ende. Zwischen ihm können Sie tun, was Sie wollen, ist keine spezielle Entkommen so lange erforderlich, wie Sie die Zeichenfolge )" nicht brauchen. Aber auch dies kann funktionieren, wenn Sie Ihre eigenen benutzerdefinierten Trennzeichen angeben:

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

Sie haben zwei Möglichkeiten:

  1. Verwendung von Compiler / Linker Erweiterungen kann man eine Datei in eine binäre Datei zu konvertieren, mit dem richtigen Symbolen auf den Beginn und das Ende der binären Daten zeigen. Sehen Sie diese Antwort: Binärdatei Fügen Sie mit GNU ld Linker-Skript .
  2. Konvertieren Sie Ihre Datei in eine Folge von Zeichenkonstanten, die ein Array initialisieren. Hinweis: Sie können nicht nur „und über mehrere Zeilen erstrecken tun“. Sie möchten gerne ein Linienfortsetzungszeichen (\) benötigen, Flucht " Zeichen und andere, um diese Arbeit zu machen. Einfacher, nur ein kleines Programm zu schreiben, um die Bytes in eine Sequenz wie '\xFF', '\xAB', ...., '\0' (oder verwenden Sie das Unix-Tool xxd durch eine andere Antwort beschrieben, wenn Sie es zur Verfügung haben!) Zu konvertieren:

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

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

Wo data.h wird durch

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

ok, inspiriert von Daemin der post i getestet das folgende einfache Beispiel:

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

So funktioniert es aber Daten umgeben benötigen mit Anführungszeichen.

Ich mag kayahr Antwort. Wenn Sie nicht über die Eingabedateien berühren wollen aber, und wenn Sie mit CMake können Sie die delimeter Zeichenfolgen in der Datei hinzufügen. Der folgende CMake Code zum Beispiel kopiert die Eingabedateien und hüllt ihre Inhalte 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)

sind dann in C ++ wie folgt aus:

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

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

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

Natürlich werden Sie mit vorsichtig sein, was in der Datei tatsächlich ist, um sicherzustellen, gibt es keine doppelten Anführungszeichen, dass alle geeigneten Zeichen entgangen sind, usw.

Daher könnte es einfacher sein, wenn Sie nur den Text aus einer Datei zur Laufzeit laden, oder betten Sie den Text direkt in den Code.

Wenn Sie immer noch den Text in einer anderen Datei wollen Sie es in dort haben könnten, aber es müßte dort als Zeichenfolge dargestellt werden. Sie würden den Code wie oben, aber ohne die doppelten Anführungszeichen in ihm verwenden. Zum Beispiel:

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

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

Sie müssen meine xtr Dienstprogramm, aber Sie können es mit einem bash script tun. Dies ist ein Skript, das ich bin2inc nennen. Der erste Parameter ist der Name des resultierenden char[] variable. Der zweite Parameter ist der Name der file. Der Ausgang ist mit dem C include file Dateiinhalt verschlüsselt (in Kleinbuchstaben hex) als Variablennamen. Die char array ist zero terminated und die Länge der Daten 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")';'

YOU CAN GET XTR HIER xtr (Zeichen Extrapolators) ist GPLv3

Sie können dieses mit objcopy tun:

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

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

reimplementiert ich in python3 xxd, die alle xxd der Belästigungen Festsetzung:

  • Const Korrektheit
  • string Länge Datentyp: int → size_t
  • Null-Terminierung (falls Sie könnten, dass wollen)
  • C-String kompatibel: Tropfen unsigned auf dem Array
  • .
  • Kleine, lesbare Ausgabe, wie Sie es geschrieben hätten: druckbaren ASCII ausgegeben, wie sie ist; andere Bytes Hex-codiert werden.

Hier ist das Skript, von selbst gefiltert, so können Sie sehen, 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);
}

Auch wenn es bei der Kompilierung durchgeführt werden kann (ich glaube nicht, es kann im Allgemeinen), würde der Text wahrscheinlich der vorverarbeiteten Header sein, anstatt die Dateien Inhalt wörtlich. Ich erwarte, dass Sie den Text aus der Datei zur Laufzeit laden müssen oder einen üblen Cut-n-Paste Job.

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 die Arbeit tun.

Hasturkun Antwort der xxd -i-Option ist ausgezeichnet. Wenn Sie den Konvertierungsprozess integrieren möchten (Text -> Hex-Include-Datei) direkt in Ihrem Build hinzugefügt der hexdump.c Werkzeug / Bibliothek vor kurzem eine Fähigkeit ähnlich wie xxd die Option -i (es nicht geben Sie den vollständigen Header - Sie brauchen die char-Array-Definition zur Verfügung zu stellen - aber das hat den Vorteil, dass Sie den Namen des char-Array auswählen):

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

Es ist Lizenz viel mehr „Standard“ als xxd und ist sehr liberal - ein Beispiel der Verwendung es eine init-Datei in einem Programm einbetten kann hier in den CMakeLists.txt und scheme.c Dateien zu sehen:

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

Es gibt Vor- und Nachteile sowohl generierten Dateien in Quellbäume einschließlich und Dienstprogramme zu bündeln - wie sie damit umgehen wird auf die spezifischen Ziele und Bedürfnisse Ihres Projekts ab. hexdump.c öffnet die Bündelung Option für diese Anwendung auf.

Ich denke, es ist nicht möglich, mit dem Compiler und Präprozessor ist allein. gcc ermöglicht 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 so aus:

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

Warum Sie den Text in das Programm nicht verbinden und es als eine globale Variable verwenden! Hier ist ein Beispiel. Ich m unter Berücksichtigung dieses mit Open GL Shader-Dateien innerhalb einer ausführbaren Datei enthalten, da GL Shader benötigt für die GPU zur Laufzeit kompiliert werden.

Ich hatte ähnliche Probleme, und für kleine Dateien, die zuvor genannte Lösung von Johannes Schaub arbeitete wie ein Charme für mich.

Doch für Dateien, die etwas größer sind, lief es in Probleme mit dem Zeichenfeld Grenze des Compilers. Daher habe ich eine kleine Codierers Anwendung, die Dateiinhalte in ein 2D-Array von Zeichen gleich große Stücke (und möglicherweise padding Nullen) umwandelt. Es erzeugt eine Ausgabe, Text-Dateien mit 2D-Array wie folgt aus:

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

, wo 4 tatsächlich eine Variable MAX_CHARS_PER_ARRAY im Geber ist. Die Datei mit dem resultierenden C-Code, genannt, zum Beispiel „main_js_file_data.h“ kann dann leicht in die C ++ inline sein, Anwendung, zum Beispiel wie folgt aus:

#include "main_js_file_data.h"

Hier ist der Quellcode des Gebers:

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

Wenn Sie bereit sind, auf einige schmutzige Tricks greifen Sie kreativ mit rohen Stringliterale und #include für bestimmte Arten von Dateien erhalten.

Zum Beispiel, sagen, dass ich einige SQL-Skripte für SQLite in meinem Projekt aufnehmen möchten, und ich will Syntax erhalten markieren, wollen aber nicht eine spezielle Build-Infrastruktur. Ich kann diese Datei test.sql haben, die gültige SQL für SQLite ist, wo -- Kommentar beginnt:

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

Und dann in meinem C ++ Code kann ich:

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

    cout << mysql << endl;
}

Die Ausgabe lautet:

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

oder einig Python-Code aus einer Datei test.py umfasst, die ein gültiger Python-Skript (weil # Kommentar in Python beginnt und pass ist eine No-op):

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

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

Und dann in dem C ++ Code:

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

    cout << mypython << endl;
}

Welche folgende Ausgabe:

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

myfunc()
#undef pass
#define pass

Es sollte möglich sein, ähnliche Tricks zu spielen für verschiedene andere Arten von Code, den Sie wollen als String könnte aufzunehmen. Unabhängig davon, ob es eine gute Idee, ich bin nicht sicher. Es ist so eine Art ordentlich Hack aber wahrscheinlich nicht etwas, das man in realen Produktionscode wollen würde. Könnte allerdings für ein Wochenende Hack Projekt ok sein.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top