"#Include" un file di testo in un programma C come char []


130

C'è un modo per includere un intero file di testo come stringa in un programma C in fase di compilazione?

qualcosa di simile a:

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

ottenere un piccolo programma che stampa su stdout "Questo è un piccolo file di testo"

Al momento ho usato uno script python hacker, ma è brutto e limitato a un solo nome di variabile, puoi dirmi un altro modo per farlo?


Dai un'occhiata qui per leggere un file in un carattere []. /programming/410943/reading-a-text-file-into-an-array-in-c Ecco alcuni suggerimenti per l'utilizzo delle macro del preprocessore C. http://gcc.gnu.org/onlinedocs/cpp/Macros.html
Daniel A. White

3
Perchè vuoi fare questo? Perché non leggere il file in fase di esecuzione? (Risposta: forse perché è difficile sapere dove si trova il file in fase di esecuzione, o forse perché dovrebbe esserci un solo file da installare.)
Jonathan Leffler

o, forse, il file di testo è disponibile solo al momento della compilazione, come il codice sorgente.
TMS

1
A volte vuoi accedere ai dati come file separati in fase di sviluppo ma hai il contenuto compilato nel tuo binario. L'esempio è l'esecuzione di un server Web su un Arduino che non ha accesso alla memoria locale. Vuoi mantenere separati i tuoi file html per modificarli ma al momento della compilazione devono esistere come stringhe nel tuo sorgente.
Geordie il

Risposte:


134

Suggerirei di usare (unix util) xxd per questo. puoi usarlo in questo modo

$ echo hello world > a
$ xxd -i a

uscite:

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

18
Solo una nota: il carattere [] creato da xxd non ha terminazione NULL! quindi faccio $ xxd -i <file.txt> file.xxd $ echo ', 0' >> file.xxd e nel char main.c file_content [] = {#include "file.xxd"};

2
Non ho mai saputo di xxd. È meraviglioso!

1
@eSKay: questo deriva direttamente dall'output di xxd, come dice la risposta. il nome dell'array è il nome file di input. se si esegue il piping dei dati anziché utilizzare un file di input, verrà visualizzato un elenco di valori esadecimali (senza la dichiarazione dell'array o la variabile len).
Hasturkun,

4
È estremamente utile quando si incorporano shader GLSL.
Linello,

5
Un altro modo per aggiungere la terminazione 0x00 a xxd ha prodotto il codice C:xxd -i file.txt | sed 's/\([0-9a-f]\)$/\0, 0x00/' > file.h
vleo,

104

La domanda riguardava C, ma nel caso in cui qualcuno provi a farlo con C ++ 11, allora può essere fatto solo con piccole modifiche al file di testo incluso grazie ai nuovi valori letterali non elaborati :

In C ++ fai questo:

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

Nel file di testo, procedere come segue:

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

Quindi ci deve essere solo un prefisso nella parte superiore del file e un suffisso alla fine di esso. In mezzo puoi fare quello che vuoi, non è necessario scappare se non hai bisogno della sequenza di caratteri )". Ma anche questo può funzionare se specifichi il tuo delimitatore personalizzato:

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

5
Grazie, ho scelto il metodo proposto qui per incorporare lunghi frammenti di sql nel mio codice C ++ 11. Questo mi permette di mantenere l'SQL separato in modo pulito nei suoi file e modificarli con il controllo della sintassi, l'evidenziazione ecc
Appropriati

1
Questo è molto vicino a quello che voglio. Soprattutto il delimitatore definito dall'utente. Molto utile. Voglio fare un passo ulteriore: c'è un modo per rimuovere completamente il prefisso R "(e suffisso)" dal file che si desidera includere? Ho provato a definire due file chiamati bra.in e ket.in con il prefisso e il suffisso in essi, inclusi bra.in, file.txt e ket.in uno per uno. Ma il compilatore valuta il contenuto di bra.in (che è solo R "() prima di includere il file successivo. Quindi si lamenterà. Per favore fatemi sapere se qualcuno sa come ottenere il prefisso e il suffisso da file.txt. Grazie.
TMS

Immagino che C ++ non consentirebbe R "(<newline> #include ...)"? Sarebbe bello avere il file in fase di compilazione in fase di compilazione per non richiedere alcuna codifica in alcun modo .... vale a dire json o xml o csv o cosa no ..
Brian Chrisman,

Puoi rendere il testo del letterale grezzo un po 'più leggibile se usi 1+R"...come delimitatore iniziale invece di R"..., e quindi anteporre una nuova riga prima Line 1. Questo trasformerà l'espressione da un array a un puntatore, ma non è un problema in questo caso, poiché stai inizializzando un puntatore, non un array.
Ruslan,

14

Hai due possibilità:

  1. Utilizzare le estensioni del compilatore / linker per convertire un file in un file binario, con simboli appropriati che indicano l'inizio e la fine dei dati binari. Vedi questa risposta: Includi il file binario con lo script GNU ld linker .
  2. Converti il ​​tuo file in una sequenza di costanti di caratteri che possono inizializzare un array. Nota che non puoi semplicemente "" e estendere più righe. Avresti bisogno di un carattere di continuazione di riga ( \), di "caratteri di escape e altri per farlo funzionare. È più semplice scrivere un piccolo programma per convertire i byte in una sequenza simile '\xFF', '\xAB', ...., '\0'(o utilizzare lo strumento unix xxddescritto da un'altra risposta, se disponibile!):

Codice:

#include <stdio.h>

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

(non testato). Quindi fa:

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

Dove data.h è generato da

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

1
l'ultima riga dovrebbe probabilmente leggere "cat file.bin | ./bin2c> data.h" o "./bin2c <file.bin> data.h"
Hasturkun

Ho usato codeproject.com/Tips/845393/… per creare un file esadecimale (su Windows) da un file binario e quindi ho usato il tuo suggerimento di char my_file[] = { #include my_large_file.h };Grazie!
Someone Somewhere

bin2cnon è lo stesso bin2c di debian's hxtools, attenzione
ThorSummoner,

o se lo è, l'invocazione è molto più strana ora:bin2c -H myoutput.h myinput1.txt myinputN.txt
ThorSummoner,

9

ok, ispirato al post di Daemin, ho testato il seguente semplice esempio:

a.data:

"this is test\n file\n"

test.c:

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

gcc -E output test.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;
}

Quindi funziona ma richiede dati racchiusi tra virgolette.


Questo è ciò a cui alludevo nell'ultima parte della mia risposta.
Daemin,

citazione, o come si chiama, perdona il mio inglese
Ilya,

Ciò richiede che i dati siano sottoposti a escape in C. Non penso che sia quello che il post sta cercando. Se questo avesse una specie di macro di inclusione che ha evaso il contenuto del file in C, ciò andrebbe bene.
Brian Chrisman,

8

Mi piace la risposta di Kayahr. Se tuttavia non si desidera toccare i file di input e se si utilizza CMake , è possibile aggiungere le sequenze di caratteri delimitatori sul file. Il seguente codice CMake, ad esempio, copia i file di input e li avvolge di conseguenza:

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)

Quindi includere in c ++ in questo modo:

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

5

Puoi farlo usando objcopy:

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

Ora hai un file oggetto che puoi collegare al tuo eseguibile che contiene simboli per l'inizio, la fine e la dimensione del contenuto da myfile.txt.


1
sei in grado di dirci quali saranno i nomi dei simboli?
Mark Ch

@MarkCh: secondo i documenti, i nomi dei simboli sono generati dal nome file di input.
John Zwinck,

Immagino che questo non funzionerà su macchine non x86-64, vero?
ThorSummoner,


2

Hai bisogno della mia xtrutility ma puoi farlo con a bash script. Questo è uno script che chiamo bin2inc. Il primo parametro è il nome del risultato char[] variable. Il secondo parametro è il nome di file. L'output è C include filecon il contenuto del file codificato (in minuscolo hex) come nome della variabile indicato. Il char arrayè zero terminated, e la lunghezza dei dati è memorizzato in$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")';'

PUOI OTTENERE XTR QUI xtr (character eXTRapolator) è GPLV3


2

Se sei disposto a ricorrere ad alcuni trucchi sporchi puoi diventare creativo con valori letterali di stringa grezzi e #includeper alcuni tipi di file.

Ad esempio, supponiamo che io voglia includere alcuni script SQL per SQLite nel mio progetto e desidero ottenere l'evidenziazione della sintassi ma non voglio alcuna infrastruttura di build speciale. Posso avere questo file test.sqlche è SQL valido per SQLite dove --inizia un commento:

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

E poi nel mio codice C ++ posso avere:

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

    cout << mysql << endl;
}

L'output è:

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

O per includere un po 'di codice Python da un file test.pyche è uno script Python valido (perché #avvia un commento in Python ed passè un no-op):

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

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

E poi nel codice C ++:

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

    cout << mypython << endl;
}

Che produrrà:

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

myfunc()
#undef pass
#define pass

Dovrebbe essere possibile giocare trucchi simili per vari altri tipi di codice che potresti voler includere come stringa. Non è sicuro che sia una buona idea. È una specie di hack pulito ma probabilmente non è qualcosa che vorresti nel vero codice di produzione. Potrebbe andare bene per un progetto di hack del fine settimana però.


Ho usato questo approccio per inserire anche OpenGL Shader in file di testo!
yano,

1

Ho reimplementato xxd in python3, risolvendo tutti i fastidi di xxd:

  • Costante correttezza
  • tipo di dati lunghezza stringa: int → size_t
  • Terminazione nulla (nel caso in cui si desideri)
  • C string compatibile: rilascia unsignedsull'array.
  • Output più piccolo e leggibile, come l'avresti scritto: ASCII stampabile viene prodotto così com'è; altri byte sono codificati in esadecimale.

Ecco lo script, filtrato da solo, quindi puoi vedere cosa fa:

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;

Utilizzo (questo estrae lo script):

#include <stdio.h>

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

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

1

Cosa potrebbe funzionare se fai qualcosa del tipo:

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

Ovviamente dovrai stare attento a ciò che è effettivamente nel file , assicurandoti che non ci siano doppie virgolette, che tutti i caratteri appropriati siano sfuggiti, ecc.

Pertanto, potrebbe essere più semplice caricare semplicemente il testo da un file in fase di esecuzione o incorporare il testo direttamente nel codice.

Se volessi ancora il testo in un altro file, potresti averlo lì dentro, ma dovrebbe essere rappresentato lì come una stringa. Dovresti usare il codice come sopra ma senza le doppie virgolette. Per esempio:

file.txt

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

main.cpp

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

Quindi fondamentalmente avere una stringa di stile C o C ++ in un file di testo che includi. Renderebbe il codice più pulito perché non c'è questa enorme quantità di testo all'inizio del file.


3
Bella idea ma non funzionerà, o hai un errore perché il valore letterale include una nuova riga o la parte #include verrà letta come una stringa e non eseguita, dannatamente se lo fai e dannatamente se non lo fai .. .
Motti

1
@Motti: d'accordo - come scritto, sintatticamente non valido C. L'idea è interessante - il Pre-Processore C è logicamente una fase separata - ma la pratica è che non si stacca da terra perché ogni riga nel file incluso avrebbe per finire con una barra rovesciata, ecc.
Jonathan Leffler

2
Humm. Mi sembra che non dovresti aver bisogno della barra rovesciata poiché la maggior parte dei compilatori concatenerà stringhe adiacenti insieme
EvilTeach

la cosa con questa risposta è ... se fosse così semplice, non credo che l'OP avrebbe mai posto la domanda! -1 perché la presenza di questa risposta incoraggia leggermente le persone a perdere tempo a provare qualcosa che non funziona. Penso che potremmo rimuovere il downvote se cambiassi "Cosa potrebbe funzionare" in "Per riferimento, questo non funziona"
Mark Ch

@JonathanLeffler Dopo l'esecuzione del preprocessore, dovrebbe essere valido C o C ++ in base alla formattazione di file.txt.
Daemin,

0

Anche se può essere fatto in fase di compilazione (non credo che possa in generale), il testo sarebbe probabilmente l'intestazione preelaborata anziché il contenuto dei file alla lettera. Mi aspetto che dovrai caricare il testo dal file in fase di esecuzione o fare un brutto lavoro di taglia e incolla.


0

La risposta di Hasturkun usando l'opzione xxd -i è eccellente. Se vuoi incorporare il processo di conversione (testo -> hex include file) direttamente nella tua build, lo strumento / libreria hexdump.c ha recentemente aggiunto una funzionalità simile all'opzione -i di xxd (non ti dà l'intestazione completa - devi per fornire la definizione di array di caratteri, ma questo ha il vantaggio di consentire di scegliere il nome dell'array di caratteri):

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

La sua licenza è molto più "standard" di xxd ed è molto liberale - un esempio di come usarlo per incorporare un file init in un programma può essere visto qui nei file CMakeLists.txt e schema.c:

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

Esistono vantaggi e svantaggi sia nell'includere i file generati negli alberi dei sorgenti sia nelle utilità di raggruppamento: il modo in cui gestirli dipenderà dagli obiettivi e dalle esigenze specifici del progetto. hexdump.c apre l'opzione di raggruppamento per questa applicazione.


0

Penso che non sia possibile solo con il compilatore e il preprocessore. gcc lo consente:

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

Ma sfortunatamente non questo:

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

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

L'errore è:

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

Ho guardato, come mi hai chiesto di guardare. Non vedo alcuna nuova informazione nella tua risposta (informazioni che non si trovano in altre risposte), oltre a un riferimento a /etc/hostnamecome un modo per incorporare il nome della macchina di compilazione nella stringa, che (anche se funzionasse) non sarebbe portatile poiché Mac OS X non ha un file /etc/hostname. Si noti che l'utilizzo di nomi di macro che iniziano con un carattere di sottolineatura seguito da una lettera maiuscola utilizza un nome riservato all'implementazione, ovvero A Bad Thing ™.
Jonathan Leffler,

0

Perché non collegare il testo al programma e usarlo come variabile globale! Ecco un esempio Sto pensando di usarlo per includere file shader Open GL all'interno di un eseguibile poiché gli shader GL devono essere compilati per la GPU in fase di esecuzione.


0

Ho avuto problemi simili e per file di piccole dimensioni la soluzione di Johannes Schaub sopra menzionata ha funzionato come un incantesimo per me.

Tuttavia, per file leggermente più grandi, si sono verificati problemi con il limite dell'array di caratteri del compilatore. Pertanto, ho scritto una piccola applicazione codificatore che converte il contenuto del file in una matrice di caratteri 2D di blocchi di dimensioni uguali (e possibilmente riempiendo zeri). Produce file di testo di output con dati di array 2D come questo:

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

dove 4 è in realtà una variabile MAX_CHARS_PER_ARRAY nell'encoder. Il file con il codice C risultante, chiamato ad esempio "main_js_file_data.h", può quindi essere facilmente incorporato nell'applicazione C ++, ad esempio in questo modo:

#include "main_js_file_data.h"

Ecco il codice sorgente dell'encoder:

#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

Questo problema mi irritava e xxd non funziona per il mio caso d'uso perché ha reso la variabile chiamata qualcosa come __home_myname_build_prog_cmakelists_src_autogen quando ho provato a scriverlo, quindi ho creato un'utilità per risolvere questo esatto problema:

https://github.com/Exaeta/brcc

Genera un file sorgente e di intestazione e consente di impostare esplicitamente il nome di ciascuna variabile in modo da poterle utilizzare tramite std :: begin (arrayname) e std :: end (arrayname).

L'ho incorporato nel mio progetto cmake in questo modo:

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)

Con piccole modifiche, suppongo che potrebbe funzionare anche per C.


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

dovrebbe fare il lavoro.


Per più righe devi aggiungere \ n così: "riga 1 \ n" "riga 2 \ n"
Superfly Jon

è un po 'fuorviante, ovviamente questo richiede una certa preparazione del file di testo per aggiungere virgolette e \ n caratteri, non funziona nel caso generale
Mark Ch
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.