Littéral de chaîne multiligne C ++

416

Existe-t-il un moyen d'avoir du texte brut multi-lignes, des littéraux constants en C ++, à la Perl? Peut-être une astuce d'analyse avec #includeun fichier? Je ne peux pas penser à un, mais mon garçon, ce serait bien. Je sais que ce sera en C ++ 0x.

rlbond
la source
1
En général, vous ne voulez pas incorporer de littéraux de chaîne dans du code. Pour I18N et L10N, il est préférable de mettre des littéraux de chaîne dans un fichier de configuration qui est chargé au moment de l'exécution.
Martin York
46
Il y a suffisamment de cas où mettre des littéraux de chaîne dans le code n'est pas un problème: si la chaîne n'est pas utilisée pour la représenter à l'utilisateur; Ex: instructions SQL, noms de fichiers, noms de clés de registre, lignes de commande à exécuter, ...
mmmmmmmm
2
@Martin: Il peut cependant être utile de le savoir. Je l'ai fait pour briser des expressions rationnelles complexes, par exemple.
Boojum

Réponses:

591

Eh bien ... en quelque sorte. Le plus simple est d'utiliser simplement le fait que les littéraux de chaîne adjacents sont concaténés par le compilateur:

const char *text =
  "This text is pretty long, but will be "
  "concatenated into just a single string. "
  "The disadvantage is that you have to quote "
  "each part, and newlines must be literal as "
  "usual.";

L'indentation n'a pas d'importance, car elle n'est pas à l'intérieur des guillemets.

Vous pouvez également le faire, tant que vous prenez soin d'échapper à la nouvelle ligne intégrée. Ne pas le faire, comme ma première réponse, ne compilera pas:

const char * text2 =
  "Ici, d'un autre côté, je suis devenu fou \
et laisser le littéral s'étendre sur plusieurs lignes, \
sans prendre la peine de citer chaque ligne \
contenu. Cela fonctionne, mais vous ne pouvez pas mettre en retrait. ";

Encore une fois, notez ces barres obliques inverses à la fin de chaque ligne, elles doivent être immédiatement avant la fin de la ligne, elles échappent à la nouvelle ligne dans la source, de sorte que tout se passe comme si la nouvelle ligne n'était pas là. Vous n'obtenez pas de sauts de ligne dans la chaîne aux emplacements où vous avez eu des barres obliques inverses. Avec ce formulaire, vous ne pouvez évidemment pas mettre le texte en retrait car le retrait deviendrait alors une partie de la chaîne, le brouillant avec des espaces aléatoires.

se détendre
la source
3
On m'a dit dans le passé que la première option peut être jusqu'à l'implémentation, mais je n'ai pas encore trouvé de compilateur qui n'honore pas cette syntaxe.
Jason Mock
28
@Jason: il ne faisait pas nécessairement partie des compilateurs antérieurs à C89, mais il est défini dans C89 et est donc pris en charge essentiellement partout.
Jonathan Leffler
4
De plus, si vous voulez vraiment que la chaîne soit formatée sur plusieurs lignes en c ++ 98, remplacez simplement \ n l'espace de fin sur chaque fragment de chaîne entre guillemets. Les littéraux bruts C ++ 11 sont toujours mes préférés.
emsr
3
@unwind Notez que la nouvelle ligne à la fin de la ligne source ne fait pas partie de la chaîne, elle est juste ignorée. Si vous voulez une nouvelle ligne dans la chaîne, vous devez avoir \ n \ à la fin de la ligne.
hyde
2
Il y a un bogue désagréable dans Microsoft Visual Studio. Si vous utilisez des barres obliques inverses à la fin des lignes, il indente automatiquement le texte à l'intérieur de la chaîne.
palota
409

En C ++ 11, vous avez des littéraux de chaîne bruts. Un peu comme du texte ici dans des shells et des langages de script comme Python et Perl et Ruby.

const char * vogon_poem = R"V0G0N(
             O freddled gruntbuggly thy micturations are to me
                 As plured gabbleblochits on a lurgid bee.
              Groop, I implore thee my foonting turlingdromes.   
           And hooptiously drangle me with crinkly bindlewurdles,
Or I will rend thee in the gobberwarts with my blurlecruncheon, see if I don't.

                (by Prostetnic Vogon Jeltz; see p. 56/57)
)V0G0N";

Tous les espaces et indentations et les sauts de ligne dans la chaîne sont conservés.

Ceux-ci peuvent également être utf-8 | 16 | 32 ou wchar_t (avec les préfixes habituels).

Je dois souligner que la séquence d'échappement, V0G0N, n'est pas réellement nécessaire ici. Sa présence permettrait de mettre) "à l'intérieur de la chaîne. En d'autres termes, j'aurais pu mettre

                "(by Prostetnic Vogon Jeltz; see p. 56/57)"

(notez les guillemets supplémentaires) et la chaîne ci-dessus serait toujours correcte. Sinon j'aurais tout aussi bien pu utiliser

const char * vogon_poem = R"( ... )";

Les parens juste à l'intérieur des guillemets sont toujours nécessaires.

emsr
la source
24
C'est vraiment ce que je veux, la possibilité d'éviter les guillemets, les barres obliques inverses, les échappements et toujours avoir des sauts de ligne dans la chaîne réelle. C'est pratique pour le code intégré (par exemple les shaders ou Lua). Malheureusement, nous n'utilisons pas encore tous C ++ - 0x. :-(
mlepage
2
J'envisageais moi-même les scripts SQL et Python intégrés. J'espérais pour vous si peut-être que gcc le laisserait passer en mode C ++ 98 mais, hélas, non.
emsr
3
Je suis plus habitué au clang et au gcc. Dans ces compilateurs, vous devez définir un indicateur pour C ++ 0x ou c ++ 11. Lookin na MS website on dirait qu'ils n'ont pas encore de littéraux bruts. Je comprends que MS publiera de nouvelles mises à jour du compilateur plus rapidement à mesure que les fonctionnalités C ++ seront mises en œuvre. Recherchez le compilateur Visual C ++ de novembre 2012 CTP [ microsoft.com/en-us/download/details.aspx?id=35515] pour le dernier bord de saignement.
emsr
5
@rsethc Utilisez simplement #if 0#endifpour commenter des blocs de code. Nid aussi.
bobbogo
1
Inspiré du poème Vogon!
Thane Plummer
27

#define MULTILINE(...) #__VA_ARGS__
Consomme tout entre les parenthèses.
Remplace n'importe quel nombre de caractères d'espaces consécutifs par un seul espace.

Zlatan Stanojević
la source
1
Vous pouvez ajouter \nsi vous avez besoin de nouvelles lignes
Simon
Notez que ` (and hence \ n ) is copied literally, but "` est converti en \". Donc MULTILINE(1, "2" \3)donne "1, \"2\" \3".
Andreas Spindler
@AndreasSpindler Les guillemets et les barres obliques inverses sont échappés par des barres obliques inverses (supplémentaires) tant qu'elles apparaissent dans une chaîne ou un jeton littéral de caractère. Je ne sais pas quel est votre point. Il est illégal d'avoir une citation inégalée (double ou simple), donc les contractions ne fonctionnent pas, ou un nombre impair d'entre elles de toute façon, ce qui est probablement le plus gros inconvénient. +1 de toute façon. Les «vrais programmeurs» utilisent toujours les contractions par paires sans nouvelle ligne intermédiaire, de sorte que les guillemets simples s'équilibrent.
Potatoswatter
Le fait est qu'il a écrit "consomme tout entre parenthèses".
Andreas Spindler
25

Un moyen probablement pratique pour saisir des chaînes multilignes consiste à utiliser des macros. Cela ne fonctionne que si les guillemets et les parenthèses sont équilibrés et ne contient pas de virgules de «haut niveau»:

#define MULTI_LINE_STRING(a) #a
const char *text = MULTI_LINE_STRING(
  Using this trick(,) you don't need to use quotes.
  Though newlines and     multiple     white   spaces
  will be replaced by a single whitespace.
);
printf("[[%s]]\n",text);

Compilé avec gcc 4.6 ou g ++ 4.6, cela produit: [[Using this trick(,) you don't need to use quotes. Though newlines and multiple white spaces will be replaced by a single whitespace.]]

Notez que le ,ne peut pas être dans la chaîne, sauf s'il est contenu entre parenthèses ou guillemets. Les guillemets simples sont possibles, mais créent des avertissements du compilateur.

Edit: Comme mentionné dans les commentaires, #define MULTI_LINE_STRING(...) #__VA_ARGS__permet l'utilisation de ,.

bcmpinc
la source
Pour un projet dans lequel je voulais inclure des extraits de code lua en c ++, j'ai fini par écrire un petit script python, dans lequel j'ai entré les chaînes multilignes, et laisser cela générer un fichier source c ++.
bcmpinc
Parfait pour moi, en ajoutant une énorme chaîne flottante de plusieurs lignes à partir d'un fichier collada pour les tests unitaires. Je n'avais pas envie de mettre des devis partout, j'avais besoin d'une solution copier-coller.
Soylent Graham
7
Vous pouvez utiliser #define MULTILINE(...) #__VA_ARGS__si vous souhaitez que votre chaîne contienne des virgules.
Simon
2
Notez que cela supprimera la plupart des blancs supplémentaires (y compris tous \net \r), ce qui est pratique pour certains cas et fatal pour d'autres.
BCS
17

Vous pouvez également le faire:

const char *longString = R""""(
This is 
a very 
long 
string
)"""";
Raydelto Hernandez
la source
2
merci, c'est super, ça marche même en C. évidemment, char longString[] = R""""( This is a very long string )""""; ça marche aussi, pour moi.
catching_learner
2
Est-ce que cela commence et termine la chaîne avec une nouvelle ligne?
Tim MB
1
C'est un littéral de chaîne brut . Disponible depuis C ++ 11.
Mikolasan
15

Vous pouvez simplement faire ceci:

const char *text = "This is my string it is "
     "very long";
Eric
la source
En quoi est-ce différent de la réponse de @ unwind?
Sisir
1
@Sisir Je l'ai posté 2 minutes avant le déroulement.
Eric
Toutes mes excuses pour avoir raté cette partie. Mon +1
Sisir
10

Puisqu'une once d'expérience vaut une tonne de théorie, j'ai essayé un petit programme de test pour MULTILINE:

#define MULTILINE(...) #__VA_ARGS__

const char *mstr[] =
{
    MULTILINE(1, 2, 3),       // "1, 2, 3"
    MULTILINE(1,2,3),         // "1,2,3"
    MULTILINE(1 , 2 , 3),     // "1 , 2 , 3"
    MULTILINE( 1 , 2 , 3 ),   // "1 , 2 , 3"
    MULTILINE((1,  2,  3)),   // "(1,  2,  3)"
    MULTILINE(1
              2
              3),             // "1 2 3"
    MULTILINE(1\n2\n3\n),     // "1\n2\n3\n"
    MULTILINE(1\n
              2\n
              3\n),           // "1\n 2\n 3\n"
    MULTILINE(1, "2" \3)      // "1, \"2\" \3"
};

Compilez ce fragment avec cpp -P -std=c++11 filenamepour le reproduire.

L'astuce #__VA_ARGS__est qu'il __VA_ARGS__ne traite pas le séparateur de virgules. Vous pouvez donc le transmettre à l'opérateur de stringizing. Les espaces de début et de fin sont supprimés, puis les espaces (y compris les retours à la ligne) entre les mots sont compressés en un seul espace. Les parenthèses doivent être équilibrées. Je pense que ces lacunes expliquent pourquoi les concepteurs de C ++ 11, malgré #__VA_ARGS__, ont vu le besoin de littéraux de chaîne bruts.

Andreas Spindler
la source
9

Juste pour élucider un peu le commentaire de @ emsr dans la réponse de @ unwind, si l'on n'a pas la chance d'avoir un compilateur C ++ 11 (par exemple GCC 4.2.1), et qu'on veut incorporer les nouvelles lignes dans la chaîne (soit char * ou classe string), on peut écrire quelque chose comme ceci:

const char *text =
  "This text is pretty long, but will be\n"
  "concatenated into just a single string.\n"
  "The disadvantage is that you have to quote\n"
  "each part, and newlines must be literal as\n"
  "usual.";

Très évident, c'est vrai, mais le bref commentaire de @ emsr ne m'a pas sauté aux yeux lorsque j'ai lu ceci la première fois, j'ai donc dû le découvrir par moi-même. J'espère que j'ai sauvé quelqu'un d'autre quelques minutes.

CXJ
la source
-1
// C++11. 
std::string index_html=R"html(
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>VIPSDK MONITOR</title>
    <meta http-equiv="refresh" content="10">
</head>
<style type="text/css">
</style>
</html>
)html";
user3635122
la source
Veuillez ajouter une explication à votre réponse et pas seulement des extraits de code
Geordie
-1

Option 1. En utilisant la bibliothèque boost, vous pouvez déclarer la chaîne comme ci-dessous

const boost::string_view helpText = "This is very long help text.\n"
      "Also more text is here\n"
      "And here\n"

// Pass help text here
setHelpText(helpText);

Option 2. Si boost n'est pas disponible dans votre projet, vous pouvez utiliser std :: string_view () en C ++ moderne.

piyu2cool
la source