Mehrzeiliges C ++ - Zeichenfolgenliteral

415

Gibt es eine Möglichkeit, mehrzeilige, konstante Klartextliterale in C ++ à la Perl zu verwenden? Vielleicht ein Parsing-Trick mit #includeeiner Datei? Ich kann mir keinen vorstellen, aber Junge, das wäre schön. Ich weiß, dass es in C ++ 0x sein wird.

rlbond
quelle
1
Im Allgemeinen möchten Sie keine Zeichenfolgenliterale in Code einbetten. Für I18N und L10N ist es vorzuziehen, Zeichenfolgenliterale in eine Konfigurationsdatei einzufügen, die zur Laufzeit geladen wird.
Martin York
45
Es gibt genügend Fälle, in denen das Einfügen von Zeichenfolgenliteralen in Code kein Problem darstellt: Wenn die Zeichenfolge nicht zur Darstellung für den Benutzer verwendet wird; dh: SQL-Anweisungen, Dateinamen, Registrierungsschlüsselnamen,
auszuführende
2
@ Martin: Es kann trotzdem nützlich sein zu wissen. Ich habe es zum Beispiel getan, um komplexe Regexs aufzubrechen.
Boojum

Antworten:

591

Naja, so ungefähr. Am einfachsten ist es, nur die Tatsache zu verwenden, dass benachbarte Zeichenfolgenliterale vom Compiler verkettet werden:

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.";

Die Einrückung spielt keine Rolle, da sie nicht in den Anführungszeichen steht.

Sie können dies auch tun, solange Sie darauf achten, den eingebetteten Zeilenumbruch zu umgehen. Wenn Sie dies nicht tun, wie es meine erste Antwort getan hat, wird Folgendes nicht kompiliert:

const char * text2 =
  "Hier andererseits bin ich verrückt geworden \
und lassen Sie das Literal wirklich mehrere Zeilen überspannen, \
ohne sich die Mühe zu machen, die \ jeder Zeile zu zitieren
Inhalt. Das funktioniert, aber Sie können nicht einrücken. ";

Beachten Sie auch hier diese Backslashes am Ende jeder Zeile. Sie müssen unmittelbar vor dem Ende der Zeile stehen und der neuen Zeile in der Quelle entkommen, damit sich alles so verhält, als ob die neue Zeile nicht vorhanden wäre. An den Stellen, an denen Sie Backslashes hatten, werden keine Zeilenumbrüche in der Zeichenfolge angezeigt. Mit diesem Formular können Sie den Text offensichtlich nicht einrücken, da der Einzug dann Teil der Zeichenfolge wird und mit zufälligen Leerzeichen versehen wird.

entspannen
quelle
3
In der Vergangenheit wurde mir gesagt, dass die erste Option bis zur Implementierung reichen kann, aber ich habe noch keinen Compiler gefunden, der diese Syntax nicht berücksichtigt.
Jason Mock
28
@Jason: Es war nicht unbedingt Teil von Pre-C89-Compilern, aber es ist in C89 definiert und wird daher im Wesentlichen überall unterstützt.
Jonathan Leffler
4
Wenn Sie wirklich möchten, dass die Zeichenfolge in c ++ 98 in mehreren Zeilen formatiert wird, ersetzen Sie einfach \ n den Abschlussbereich für jedes Zeichenfolgenfragment in Anführungszeichen. C ++ 11-Rohliterale sind immer noch mein Favorit.
emsr
3
@unwind Beachten Sie, dass Zeilenumbrüche am Ende der Quellzeile nicht Teil der Zeichenfolge sind, sondern nur übersprungen werden. Wenn Sie eine neue Zeile als Teil der Zeichenfolge möchten, muss \ n \ am Ende der Zeile stehen.
Hyde
2
Es gibt einen bösen Fehler in Microsoft Visual Studio. Wenn Sie am Ende von Zeilen Backslashes verwenden, wird der Text in der Zeichenfolge automatisch eingerückt.
Palota
406

In C ++ 11 haben Sie rohe String-Literale. So ähnlich wie hier-Text in Shells und Skriptsprachen wie Python und Perl und 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";

Alle Leerzeichen und Einrückungen sowie die Zeilenumbrüche in der Zeichenfolge bleiben erhalten.

Dies können auch utf-8 | 16 | 32 oder wchar_t sein (mit den üblichen Präfixen).

Ich möchte darauf hinweisen, dass die Escape-Sequenz V0G0N hier eigentlich nicht benötigt wird. Seine Anwesenheit würde es ermöglichen, in die Saite zu setzen. Mit anderen Worten, ich hätte es setzen können

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

(zusätzliche Anführungszeichen beachten) und die obige Zeichenfolge wäre immer noch korrekt. Sonst hätte ich es genauso gut gebrauchen können

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

Die Parens direkt in den Anführungszeichen werden noch benötigt.

emsr
quelle
23
Dies ist wirklich das, was ich möchte, die Fähigkeit, Anführungszeichen, Backslash-Ns, Escapezeichen zu vermeiden und immer noch Zeilenumbrüche in der eigentlichen Zeichenfolge anzuzeigen. Dies ist praktisch für eingebetteten Code (z. B. Shader oder Lua). Leider verwenden wir noch nicht alle C ++ - 0x. :-(
Mlepage
2
Ich habe dies selbst für eingebettete SQL- und Python-Skripte in Betracht gezogen. Ich hatte für Sie gehofft, wenn gcc es vielleicht im C ++ 98-Modus durchgehen lassen würde, aber leider nein.
Emsr
3
Ich bin eher daran gewöhnt zu klirren und zu gcc. In diesen Compilern müssen Sie ein Flag für C ++ 0x oder c ++ 11 setzen. Auf einer MS-Website sieht es so aus, als hätten sie noch keine rohen Literale. Ich verstehe, dass MS neue Compiler-Updates schneller veröffentlichen wird, wenn C ++ - Funktionen implementiert werden. Suchen Sie nach Visual C ++ Compiler November 2012 CTP [ microsoft.com/en-us/download/details.aspx?id=35515], um die neuesten Informationen zu erhalten.
Emsr
5
@rsethc Verwenden Sie einfach #if 0... #endif, um Codeblöcke zu kommentieren. Nester auch.
Bobbogo
1
Inspiriert vom Vogon-Gedicht!
Thane Plummer
27

#define MULTILINE(...) #__VA_ARGS__
Verbraucht alles zwischen den Klammern.
Ersetzt eine beliebige Anzahl aufeinanderfolgender Leerzeichen durch ein einzelnes Leerzeichen.

Zlatan Stanojević
quelle
1
Sie können hinzufügen, \nwenn Sie Zeilenumbrüche benötigen
Simon
Beachten Sie, dass ` (and hence \ n ) is copied literally, but "` in umgewandelt wird \". So MULTILINE(1, "2" \3)ergibt sich "1, \"2\" \3".
Andreas Spindler
@AndreasSpindler Anführungszeichen und Backslashes werden durch (zusätzliche) Backslashes maskiert, solange sie in einem String- oder Zeichenliteral-Token erscheinen. Ich bin mir nicht sicher, worum es dir geht. Es ist illegal, ein unübertroffenes Angebot (doppelt oder einfach) zu haben, daher funktionieren Kontraktionen nicht oder eine ungerade Anzahl von ihnen, was wahrscheinlich der größte Nachteil ist. +1 sowieso. "Echte Programmierer" verwenden Kontraktionen immer paarweise ohne dazwischenliegende Zeilenumbrüche, damit die einfachen Anführungszeichen ausgeglichen sind.
Potatoswatter
Der Punkt ist, dass er schrieb "verbraucht alles zwischen Klammern".
Andreas Spindler
25

Eine wahrscheinlich bequeme Möglichkeit, mehrzeilige Zeichenfolgen einzugeben, ist die Verwendung von Makros. Dies funktioniert nur, wenn Anführungszeichen und Klammern ausgeglichen sind und keine Kommas der obersten Ebene enthalten:

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

Kompiliert mit gcc 4.6 oder g ++ 4.6 ergibt dies: [[Using this trick(,) you don't need to use quotes. Though newlines and multiple white spaces will be replaced by a single whitespace.]]

Beachten Sie, dass das ,nicht in der Zeichenfolge enthalten sein kann, es sei denn, es ist in Klammern oder Anführungszeichen enthalten. Einfache Anführungszeichen sind möglich, erstellen jedoch Compiler-Warnungen.

Bearbeiten: Wie in den Kommentaren erwähnt, #define MULTI_LINE_STRING(...) #__VA_ARGS__erlaubt die Verwendung von ,.

bcmpinc
quelle
Für ein Projekt, in das ich einige Lua-Code-Schnipsel in C ++ aufnehmen wollte, schrieb ich schließlich ein kleines Python-Skript, in das ich die mehrzeiligen Zeichenfolgen eingab und eine C ++ - Quelldatei generierte.
bcmpinc
Perfekt für mich, indem ich eine riesige mehrzeilige Float-List-Zeichenfolge aus einer Collada-Datei zum Testen von Einheiten hinzufüge. Ich wollte nicht überall Anführungszeichen setzen, ich brauchte eine Copy & Paste-Lösung.
Soylent Graham
7
Sie können verwenden, #define MULTILINE(...) #__VA_ARGS__wenn Ihre Zeichenfolge Kommas enthalten soll.
Simon
2
Beachten Sie, dass dadurch die meisten zusätzlichen Whitesapce (einschließlich aller \nund \r) entfernt werden, was in einigen Fällen praktisch und in anderen tödlich ist.
BCS
17

Sie können dies auch tun:

const char *longString = R""""(
This is 
a very 
long 
string
)"""";
Raydelto Hernandez
quelle
2
danke, das ist toll, funktioniert sogar in C. offensichtlich, char longString[] = R""""( This is a very long string )""""; funktioniert auch für mich.
struggling_learner
2
Beginnt und beendet dies die Zeichenfolge mit einer neuen Zeile?
Tim MB
1
Es ist ein rohes String-Literal . Verfügbar seit C ++ 11.
Mikolasan
15

Sie können dies einfach tun:

const char *text = "This is my string it is "
     "very long";
Eric
quelle
Wie unterscheidet es sich von der Antwort von @ unwind?
Sisir
1
@ Schwester Ich habe es 2 Minuten vor dem Abwickeln gepostet.
Eric
Entschuldigung für das Fehlen dieses Teils. Mein +1
Sisir
10

Da eine Unze Erfahrung eine Menge Theorie wert ist, habe ich ein kleines Testprogramm ausprobiert für 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"
};

Kompilieren Sie dieses Fragment mit, um es cpp -P -std=c++11 filenamezu reproduzieren.

Der Trick dahinter #__VA_ARGS__ist, dass __VA_ARGS__das Komma-Trennzeichen nicht verarbeitet wird. Sie können es also an den Stringing-Operator übergeben. Führende und nachfolgende Leerzeichen werden abgeschnitten, und Leerzeichen (einschließlich Zeilenumbrüche) zwischen Wörtern werden dann auf ein einzelnes Leerzeichen komprimiert. Klammern müssen ausgewogen sein. Ich denke, diese Mängel erklären, warum die Designer von C ++ 11 trotz #__VA_ARGS__der Notwendigkeit roher String-Literale sahen.

Andreas Spindler
quelle
9

Nur um den Kommentar von @ emsr in der Antwort von @ unwind ein wenig zu erläutern, wenn man nicht das Glück hat, einen C ++ 11-Compiler zu haben (z. B. GCC 4.2.1) und die Zeilenumbrüche in den String einbetten möchte (entweder char * oder Klassenzeichenfolge) kann man so etwas schreiben:

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.";

Sehr offensichtlich, stimmt, aber der kurze Kommentar von @ emsr ist mir beim ersten Lesen nicht aufgefallen, also musste ich das selbst herausfinden. Hoffentlich habe ich jemand anderem ein paar Minuten gespart.

CXJ
quelle
-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
quelle
Bitte fügen Sie Ihrer Antwort eine Erklärung hinzu und nicht nur Code-Schnipsel
Geordie
-1

Option 1. Mit der Boost-Bibliothek können Sie die Zeichenfolge wie folgt deklarieren

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. Wenn in Ihrem Projekt kein Boost verfügbar ist, können Sie std :: string_view () in modernem C ++ verwenden.

piyu2cool
quelle