149 votes

"#inclure" un fichier texte dans un programme C comme un char[]

Existe-t-il un moyen d'inclure un fichier texte entier sous forme de chaîne dans un programme C au moment de la compilation ?

quelque chose comme :

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

obtenir un petit programme qui imprime sur stdout "Ceci est un petit fichier texte"

Pour le moment, j'ai utilisé un script pirate de python, mais c'est moche et limité à un seul nom de variable, pouvez-vous me dire une autre façon de le faire ?

2voto

user2394284 Points 97

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

  • La correction des constantes
  • longueur de la chaîne de caractères type de données : int size_t
  • Terminaison nulle (au cas où vous le souhaiteriez)
  • C chaîne compatible : Drop unsigned sur le tableau.
  • Un résultat plus petit et lisible, tel que vous l'auriez écrit : L'ascii imprimable est affiché 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 (ceci extrait le script) :

#include <stdio.h>

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

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

1voto

ET3D Points 21

Voici une astuce que j'utilise pour Visual C++. J'ajoute l'événement de pré-construction suivant (où fichier.txt est l'entrée et fichier_txt.h est la sortie) :

@(
  echo const char text[] = R"***(
  type file.txt
  echo ^^^)***";
) > file_txt.h

J'inclus ensuite file_txt.h là où j'en ai besoin.

Ce n'est pas parfait, car cela ajoute \n au départ et \n ^ à la fin, mais ce n'est pas un problème à gérer et j'aime la simplicité de cette solution. Si quelqu'un peut l'affiner pour se débarrasser des caractères supplémentaires, ce serait bien.

0voto

Daemin Points 5651

Quoi pourrait le travail 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 faites attention à ce qui se trouve réellement dans le fichier en s'assurant qu'il n'y a pas de guillemets doubles, que tous les caractères appropriés sont échappés, etc.

Il serait donc plus facile si vous charger le texte à partir d'un fichier au moment de l'exécution. ou intégrer le texte directement dans le code.

Si vous voulez toujours avoir le texte dans un autre fichier, vous pouvez l'y placer, mais il devra y être représenté sous forme de chaîne. Vous utiliserez le même code que ci-dessus, mais sans les guillemets. Par exemple :

file.txt

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

main.cpp

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

En gros, il s'agit d'avoir une chaîne de style C ou C++ dans un fichier texte que vous incluez. Cela rendrait le code plus ordonné car il n'y aurait pas cette énorme quantité de texte au début du fichier.

0voto

Daniel Paull Points 4225

Même si cela peut être fait au moment de la compilation (je ne pense pas que ce soit possible en général), le texte serait probablement l'en-tête prétraité plutôt que le contenu des fichiers mot à mot. Je pense que vous devrez charger le texte du fichier au moment de l'exécution ou faire un méchant copier-coller.

0voto

starseeker Points 31

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

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

Sa licence est beaucoup plus "standard" que celle de xxd et est très libérale - un exemple de son utilisation pour intégrer un fichier init dans un programme peut être vu dans les fichiers CMakeLists.txt et scheme.c ici :

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

Il y a des avantages et des inconvénients à inclure les fichiers générés dans les arbres source et les utilitaires de regroupement - la façon de gérer cela dépendra des objectifs et des besoins spécifiques de votre projet. hexdump.c ouvre l'option de regroupement pour cette application.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X