& # 8220; # include & # 8221; un fichier texte dans un programme C en tant que char []

StackOverflow https://stackoverflow.com/questions/410980

  •  03-07-2019
  •  | 
  •  

Question

Est-il possible d’inclure un fichier texte entier en tant que chaîne dans un programme C au moment de la compilation?

quelque chose comme:

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

obtenir un petit programme qui imprime sur la sortie standard "Ceci est un peu fichier texte "

"

Pour le moment, j’utilisais un script python hackish, mais c’est moche et limité à un seul nom de variable, pouvez-vous me dire un autre moyen de le faire?

Était-ce utile?

La solution

Je suggèrerais d'utiliser (unix util) xxd pour cela. vous pouvez l'utiliser comme si

$ echo hello world > a
$ xxd -i a

sorties:

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

Autres conseils

La question portait sur C mais si quelqu'un essaye de le faire avec C ++ 11, il ne peut le faire qu'avec de légers changements au fichier texte inclus, grâce au nouveau raw littéraux de chaîne :

En C ++, procédez comme suit:

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

Dans le fichier texte, procédez comme suit:

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

Il ne doit donc y avoir qu'un préfixe en haut du fichier et un suffixe à la fin. Entre cela, vous pouvez faire ce que vous voulez, aucune échappement spécial n'est nécessaire tant que vous n'avez pas besoin de la séquence de caractères ) " . Mais même cela peut fonctionner si vous spécifiez votre propre délimiteur personnalisé:

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

Vous avez deux possibilités:

  1. Utilisez les extensions compiler / linker pour convertir un fichier en fichier binaire, avec les symboles appropriés pointant vers le début et la fin des données binaires. Voir cette réponse: Inclure un fichier binaire avec le script de l'éditeur de liens GNU .
  2. Convertissez votre fichier en une suite de constantes de caractère pouvant initialiser un tableau. Notez que vous ne pouvez pas simplement faire " " et étendre plusieurs lignes. Vous aurez besoin d'un caractère de continuation de ligne ( \ ), de caractères d'échappement " et autres pour que cela fonctionne. Plus facile d'écrire un petit programme pour convertir les octets en une séquence comme '\ xFF', '\ xAB', ...., '\ 0' (ou utilisez l'outil unix xxd décrit par une autre réponse, si vous l’avez disponible!):

Code:

#include <stdio.h>

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

(non testé). Alors faites:

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

Où data.h est généré par

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

ok, inspiré par de Daemin's post j'ai testé l'exemple simple suivant:

a.data:

"this is test\n file\n"

test.c:

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

gcc -E test.c sortie:

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

Cela fonctionne donc, mais nécessite des données entourées de guillemets.

J'aime la réponse de Kayahr. Si vous ne souhaitez pas toucher aux fichiers d'entrée , cependant, et si vous utilisez CMake , vous pouvez ajouter les séquences de caractères de délimètre au fichier. Le code CMake suivant, par exemple, copie les fichiers d’entrée et encapsule leur contenu en conséquence:

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)

Ensuite, incluez dans c ++ comme ceci:

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

Ce qui pourrait fonctionner, c’est si vous faites quelque chose comme:

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

Bien sûr, vous devrez faire attention à ce qui est réellement dans le fichier, en vous assurant qu'il n'y a pas de guillemets doubles, que tous les caractères appropriés sont échappés, etc.

Par conséquent, il serait peut-être plus simple de charger le texte d'un fichier à l'exécution ou de l'intégrer directement dans le code.

Si vous vouliez toujours le texte dans un autre fichier, vous pourriez l'avoir dedans, mais il devrait y être représenté sous forme de chaîne. Vous utiliseriez le code comme ci-dessus, mais sans les guillemets. Par exemple:

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

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

Vous avez besoin de mon utilitaire xtr mais vous pouvez le faire avec un script bash . C’est un script que j’appelle bin2inc . Le premier paramètre est le nom de la variable char [] résultante . Le deuxième paramètre est le nom du fichier . La sortie est C inclure le fichier avec le contenu du fichier codé (en minuscule hex ) en tant que nom de variable. Le tableau de caractères est terminé par un zéro , et la longueur des données est stockée dans $ nomVariable_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")';'

VOUS POUVEZ OBTENIR UN XTR ICI xtr (caractère eXTRapolator) est GPLV3

Vous pouvez le faire en utilisant objcopy :

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

Vous avez maintenant un fichier objet que vous pouvez lier à votre exécutable et qui contient des symboles pour le début, la fin et la taille du contenu de myfile.txt .

J'ai réimplémenté xxd en python3, corrigeant tous les ennuis de xxd:

  • Const. correct
  • type de longueur de chaîne: int ? & nbsp; size_t
  • Fin nulle (au cas où vous le souhaiteriez)
  • Chaîne C compatible: Supprimez unsigned sur le tableau.
  • Sortie plus petite et lisible, comme vous l’auriez écrit: ascii imprimable est sorti tel quel; les autres octets sont codés en hexadécimal.

Voici le script, filtré par lui-même, pour que vous puissiez voir ce qu'il fait:

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;

Utilisation (cela extrait le script):

#include <stdio.h>

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

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

Même si cela peut être fait au moment de la compilation (je ne pense pas que ce soit le cas en général), le texte serait probablement l'en-tête prétraité plutôt que le contenu du fichier proprement dit. Je pense que vous devrez charger le texte du fichier au moment de l’exécution ou effectuer un mauvais travail couper-coller.

dans x.h

"this is a "
"buncha text"

dans main.c

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

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

    return 0
}

devrait faire le travail.

La réponse de Hasturkun en utilisant l'option xxd -i est excellente. Si vous souhaitez incorporer le processus de conversion (texte - > hex include fichier) directement à votre construction, l'outil / bibliothèque hexdump.c a récemment ajouté une fonctionnalité similaire à l'option -i de xxd (elle ne vous donne pas l'en-tête complet). besoin de fournir la définition du tableau de caractères - mais cela présente l’avantage de vous permettre de choisir le nom du tableau de caractères):

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

Sa licence est beaucoup plus "standard". que xxd et est très libéral - un exemple d'utilisation de ce fichier pour incorporer un fichier init dans un programme est visible dans les fichiers CMakeLists.txt et scheme.c ici:

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

Il existe des avantages et des inconvénients à inclure des fichiers générés dans les arborescences source et des utilitaires de regroupement. La façon de les gérer dépend des objectifs et des besoins spécifiques de votre projet. hexdump.c ouvre l'option de regroupement pour cette application.

Je pense que ce n'est pas possible avec le compilateur et le préprocesseur uniquement. gcc permet cela:

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

Mais malheureusement pas ceci:

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

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

L'erreur est:

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

Pourquoi ne pas lier le texte au programme et l’utiliser comme variable globale! Voici un exemple. I ' m envisagez de l'utiliser pour inclure les fichiers Open GL shader dans un exécutable, car les shaders GL doivent être compilés pour le GPU au moment de l'exécution.

J'ai eu des problèmes similaires, et pour les petits fichiers, la solution susmentionnée de Johannes Schaub a fonctionné à merveille pour moi.

Cependant, pour les fichiers un peu plus volumineux, il y avait un problème avec la limite de tableau de caractères du compilateur. Par conséquent, j’ai écrit une petite application de codage qui convertit le contenu du fichier en un tableau de caractères 2D composé de morceaux de taille égale (et éventuellement de zéros de remplissage). Il produit des fichiers texte en sortie avec des données de tableau 2D telles que:

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

où 4 est en réalité une variable MAX_CHARS_PER_ARRAY dans le codeur. Le fichier contenant le code C résultant, appelé par exemple " main_js_file_data.h " peut alors être facilement intégré à l’application C ++, par exemple comme ceci:

#include "main_js_file_data.h"

Voici le code source de l'encodeur:

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

Si vous êtes prêt à recourir à de mauvaises astuces, vous pouvez faire preuve de créativité avec les littéraux de chaîne bruts et #include pour certains types de fichiers.

Par exemple, disons que je souhaite inclure des scripts SQL pour SQLite dans mon projet et que je souhaite obtenir la coloration syntaxique, mais que je ne souhaite aucune infrastructure de construction particulière. Je peux avoir ce fichier test.sql qui est valide SQL pour SQLite où - commence un commentaire:

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

Et puis dans mon code C ++, je peux avoir:

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

    cout << mysql << endl;
}

Le résultat est:

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

Ou pour inclure du code Python d'un fichier test.py qui est un script Python valide (car # démarre un commentaire en Python et passe est un non-op):

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

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

Et ensuite dans le code C ++:

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

    cout << mypython << endl;
}

Quel sera le résultat:

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

myfunc()
#undef pass
#define pass

Il devrait être possible de jouer à des astuces similaires pour divers autres types de code que vous pourriez vouloir inclure en tant que chaîne. Que ce soit ou non une bonne idée, je ne suis pas sûr. C'est une sorte de bidouille soignée mais probablement pas quelque chose que vous voudriez dans un code de production réel. Pourrait être bon pour un projet de bidouille de week-end cependant.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top