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

y a-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:

  • fichier.txt:

    This is
    a little
    text file
    
  • principal.c:

    #include <stdio.h>
    int main(void) {
       #blackmagicinclude("file.txt", content)
       /*
       equiv: char[] content = "This isna littlentext file";
       */
       printf("%s", content);
    }
    

obtenir un petit programme qui imprime sur stdout "C'est un peu fichier texte"

en ce moment j'ai utilisé un hackish Python script, mais c'est cul-moche et limité à une seule variable nom, pouvez-vous me dire une autre façon de le faire?

100
demandé sur Brian Tompsett - 汤莱恩 2009-01-04 16:37:56

15 réponses

je suggère d'utiliser (unix util) xxd pour cela. vous pouvez l'utiliser comme

$ 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;
108
répondu Hasturkun 2009-01-04 13:56:22

la question portait sur C mais dans le cas où quelqu'un essaie de le faire avec C++11 alors il peut être fait avec seulement peu de changements au fichier de texte inclus grâce à la nouvelle lignes brutes littérales :

en C++ Faites ceci:

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

dans le fichier texte faites ceci:

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 elle, vous pouvez faire ce que vous voulez, pas de spécial échappement 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)====="
82
répondu kayahr 2015-03-07 18:14:34

vous avez deux possibilités:

  1. utilisez les extensions compilateur/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 le fichier binaire avec GNU ld linker script .
  2. Convertissez votre fichier en une séquence de constantes de caractères qui peuvent initialiser un tableau. Remarque vous ne pouvez pas tout "" et s'étendent sur plusieurs lignes. Vous besoin d'un caractère de continuation de ligne ( \ ), échapper " caractères et d'autres pour faire ce travail. Plus facile d'écrire un petit programme pour convertir les octets en une séquence comme '\xFF', '\xAB', ...., '" 151950920 "' (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é). Puis faire:

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

où données.h est généré par

cat file.bin | ./bin2c > data.h
14
répondu Johannes Schaub - litb 2017-05-23 11:55:03

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

A. données:

"this is test\n file\n"

test.c:

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

test gcc-E.sortie c:

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

donc ça marche mais il faut des données entourées de guillemets.

8
répondu Ilya 2017-05-23 11:47:32

Ce qui pourrait fonctionner 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 avec ce qui est réellement dans le fichier, en s'assurant qu'il n'y a pas de guillemets, que tous les caractères appropriés sont échappés, etc.

par conséquent, il pourrait être plus facile si vous chargez simplement le texte à partir d'un fichier à l'exécution, ou intégrer le texte directement dans le code.

Si vous vouliez toujours le texte dans un autre fichier, vous pouvez avoir là-bas, mais il devrait être représenté comme une chaîne de caractères. Vous utilisez le code ci-dessus, mais sans les doubles quotes. Par exemple:

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

int main()
{
    const char* text =
#include "file.txt"
;
    printf("%s", text);
    return 0;
}
4
répondu Daemin 2009-01-04 22:15:22

vous avez besoin de mon utilitaire xtr mais vous pouvez le faire avec un bash script . C'est un script que j'appelle bin2inc . Le premier paramètre est le nom du résultat char[] variable . Le second paramètre est le nom du file . La sortie est C include file avec le contenu du fichier encodé (en minuscules hex ) comme nom de variable. Le char array est zero terminated , et la longueur des données est stockée dans $variableName_length

#!/bin/bash

fileSize ()

{

    [ -e "" ]  && {

        set -- `ls -l ""`;

        echo ;

    }

}

echo unsigned char '[] = {'
./xtr -fhex -p 0x -s ', ' < "";
echo '0x00'
echo '};';
echo '';
echo unsigned long int _length = $(fileSize "")';'

VOUS POUVEZ OBTENIR XTR ICI xtr (caractère extrapolateur) est la GPLV3

2
répondu 2012-12-10 04:16:20

vous pouvez le faire en utilisant objcopy :

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

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

1
répondu John Zwinck 2017-09-14 14:37:24

j'aime la réponse de kayahr. si vous ne voulez pas toucher les fichiers d'entrée cependant, et si vous utilisez CMake , vous pouvez ajouter les séquences de caractères delimétriques sur le fichier. Le code CMake suivant, par exemple, copie les fichiers d'entrée et enveloppe 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)

puis inclure en C++ comme ceci:

constexpr char *test =
#include "generated/cool.frag"
;
1
répondu Martin R. 2017-12-13 23:23:47

je réimplémentée xxd en python3, la fixation de l'ensemble de xxd de désagréments:

  • Constance rectitude
  • longueur de chaîne Type de données: int → size_t
  • de fin Null (dans le cas où vous pourriez voulez que)
  • compatible C string: Drop unsigned sur le tableau.
  • sortie plus petite, lisible, comme vous l'auriez écrit: L'ASCII imprimable est sortie telle quelle; les autres octets sont encodés hex -.

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;
"151930920 d'Utilisation" (ce extraits du script):

#include <stdio.h>

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

int main()
{
    fwrite(pyxxd, 1, pyxxd_len, stdout);
}
1
répondu user2394284 2018-04-27 11:29:51

même si cela peut être fait au moment de la compilation (Je ne pense pas qu'il puisse en général), le texte serait probablement l'en-tête pré-traité plutôt que le contenu des fichiers textuellement. Je m'attends à ce que vous ayez à charger le texte du fichier à l'exécution ou faire un travail de découpage-n-paste méchant.

0
répondu Daniel Paull 2009-01-04 13:54:40

dans X. h

"this is a "
"buncha text"

.c

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

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

    return 0
}

devrait faire l'affaire.

0
répondu EvilTeach 2009-01-04 21:58:26

la réponse de Hasturkun en utilisant l'option xxd-i est excellente. Si vous voulez incorporer le processus de conversion (text -> HEX include file) directement dans votre construire l'hexdump.c outil / bibliothèque a récemment ajouté une capacité similaire à l'option-i de xxd (elle ne vous donne pas l'en - tête complet - vous devez fournir la définition du tableau char-mais qui a l'avantage de vous permettre de choisir le nom du tableau char):

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

sa licence est beaucoup plus" standard " que xxd et est très libérale - un exemple d'utilisation pour intégrer un fichier d'initialisation dans un programme peut être vu dans les listes Cmakel.txt et scheme.c fichiers 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 sources et le bundling utilitaires - comment gérer cela dépendra des objectifs et des besoins de votre projet. hexdump.c ouvre l'option de groupage pour cette application.

0
répondu starseeker 2013-04-14 04:20:10

je pense que ce n'est pas possible avec le compilateur et le préprocesseur seul. gcc permet ceci:

#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 cela:

#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"
0
répondu not-a-user 2014-04-15 13:59:29

Pourquoi ne pas lier le texte dans le programme et l'utiliser comme une variable globale! voici un exemple. j'envisage d'utiliser ceci pour inclure les fichiers GL shader ouverts dans un exécutable puisque les shaders GL doivent être compilés pour le GPU à l'exécution.

0
répondu TechDragon 2014-09-11 05:24:18

j'ai eu des problèmes similaires, et pour les petits fichiers la solution de Johannes Schaub susmentionnée a fonctionné comme un charme pour moi.

Cependant, pour les fichiers qui sont un peu plus grands, il a rencontré des problèmes avec la limite du tableau de caractères du compilateur. Par conséquent, j'ai écrit une petite application d'encodeur qui convertit le contenu de fichier en un tableau de caractères 2D de morceaux de taille égale (et peut-être des zéros de remplissage). Il produit des textfiles de sortie avec des données de tableau 2D comme ceci:

const char main_js_file_data[8][4]= {
    {'\x69','\x73','\x20','"151900920"'},
    {'\x69','\x73','\x20','"151900920"'},
    {'\x61','\x20','\x74','"151900920"'},
    {'\x65','\x73','\x74','"151900920"'},
    {'\x20','\x66','\x6f','"151900920"'},
    {'\x72','\x20','\x79','"151900920"'},
    {'\x6f','\x75','\xd','"151900920"'},
    {'\xa','"151900920"','"151900920"','"151900920"'}};

où 4 est en fait une variable MAX_CHARS_PER_ARRAY dans l'encodeur. Le fichier avec le code C résultant, appelé, par exemple "main_js_file_data.h" peut alors être facilement intégré dans 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;
}
0
répondu volzotan 2017-03-31 00:03:01