C ++ literał ciągu wielowierszowego


415

Czy jest jakiś sposób na posiadanie wieloliniowych tekstów stałych, literałów stałych w C ++, à la Perl? Może jakiś parsowanie sztuczki z #includeingowaniem pliku? Nie mogę myśleć o jednym, ale chłopaku, byłoby miło. Wiem, że będzie w C ++ 0x.


1
Zasadniczo nie chcesz osadzać literałów łańcuchowych w kodzie. W przypadku I18N i L10N zaleca się umieszczanie literałów łańcuchowych w pliku konfiguracyjnym ładowanym w czasie wykonywania.
Martin York,

45
Istnieje wystarczająca liczba przypadków, w których umieszczanie literałów ciągów w kodzie nie stanowi problemu: jeśli ciąg nie jest używany do reprezentowania go użytkownikowi; tj .: instrukcje SQL, nazwy plików, nazwy kluczy rejestru, wiersze poleceń do wykonania, ...
mmmmmmmm

2
@Martin: Jednak nadal warto wiedzieć. Zrobiłem to, na przykład, aby rozbić złożone wyrażenia regularne.
Boojum

Odpowiedzi:


591

Cóż ... W pewnym sensie. Najłatwiej jest po prostu użyć faktu, że sąsiadujące literały łańcuchowe są łączone przez kompilator:

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

Wcięcie nie ma znaczenia, ponieważ nie ma go w cudzysłowie.

Możesz to również zrobić, o ile będziesz starał się uciec z wbudowanego nowego wiersza. Nieprzestrzeganie tego, podobnie jak moja pierwsza odpowiedź, nie spowoduje kompilacji:

const char * text2 =
  „Z drugiej strony zwariowałem \
i naprawdę pozwól, aby dosłowność obejmowała kilka linii, \
bez zawracania głowy cytowaniem każdego wiersza \
zadowolony. To działa, ale nie można wcięcia. ";

Ponownie zwróć uwagę na te ukośniki odwrotne na końcu każdej linii, muszą znajdować się bezpośrednio przed końcem linii, uciekają od nowej linii w źródle, aby wszystko działało tak, jakby nowej linii nie było. W miejscach, w których wystąpiły ukośniki odwrotne, nie pojawiają się znaki nowej linii. W tym formularzu nie można oczywiście wciąć tekstu, ponieważ wcięcie stałoby się częścią łańcucha, zmieniając go losowymi spacjami.


3
W przeszłości powiedziano mi, że pierwsza opcja może zależeć od implementacji, jednak nie znalazłem jeszcze kompilatora, który nie przestrzega tej składni.
Jason Mock

28
@Jason: niekoniecznie był częścią kompilatorów sprzed C89, ale jest zdefiniowany w C89 i dlatego jest obsługiwany zasadniczo wszędzie.
Jonathan Leffler

4
Ponadto, jeśli naprawdę chcesz, aby ciąg był sformatowany w wielu wierszach w c ++ 98, po prostu zastąp \ n spacją kończącą na każdym cytowanym fragmencie ciągu. Surowe literały C ++ 11 są nadal moim ulubionym.
emsr

3
@unwind Zauważ, że nowa linia na końcu linii źródłowej nie jest częścią ciągu, jest tylko pomijana. Jeśli chcesz, aby nowa linia była częścią ciągu, musisz mieć \ n \ na końcu linii.
hyde

2
W Microsoft Visual Studio jest paskudny błąd. Jeśli użyjesz odwrotnych ukośników na końcu linii, wówczas automatycznie wcina tekst w ciągu.
palota

406

W C ++ 11 masz surowe literały łańcuchowe. Coś jak tekst tutaj w powłokach i językach skryptowych, takich jak Python, Perl i 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";

Wszystkie spacje i wcięcia oraz znaki nowej linii w łańcuchu są zachowane.

Mogą to być również utf-8 | 16 | 32 lub wchar_t (ze zwykłymi prefiksami).

Powinienem zaznaczyć, że sekwencja ucieczki, V0G0N, nie jest tutaj tak naprawdę potrzebna. Jego obecność pozwoliłaby na umieszczenie) w sznurku. Innymi słowy, mógłbym to zrobić

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

(zwróć uwagę na dodatkowe cytaty), a ciąg powyżej nadal będzie poprawny. W przeciwnym razie równie dobrze mógłbym skorzystać

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

Pareny znajdujące się w cudzysłowie są nadal potrzebne.


23
To jest właśnie to, czego chcę, możliwość unikania cudzysłowów, odwrotnego ukośnika-N, ucieczki i wciąż pojawiania się nowych wierszy w rzeczywistym ciągu. Jest to przydatne w przypadku kodu osadzonego (np. Shaderów lub Lua). Niestety nie wszyscy jeszcze używamy C ++ - 0x. :-(
mlepage

2
Sam rozważałem to dla osadzonych skryptów SQL i Python. Miałem nadzieję dla ciebie, czy może gcc pozwoli mu przesuwać się w trybie C ++ 98, ale niestety nie.
emsr

3
Jestem bardziej przyzwyczajony do bicia i gcc. W tych kompilatorach musisz ustawić flagę dla C ++ 0x lub c ++ 11. Wygląda na stronie MS wygląda na to, że nie mają jeszcze surowych literałów. Rozumiem, że MS wyda nowe aktualizacje kompilatora szybciej, gdy funkcje C ++ zostaną wdrożone. Poszukaj kompilatora CTP programu Visual C ++ z listopada 2012 r. [ Microsoft.com/en-us/download/details.aspx?id=35515], aby uzyskać najnowszą przewagę.
emsr

5
@rsethc Wystarczy użyć #if 0…, #endifaby skomentować bloki kodu. Gniazda też.
bobbogo,

1
Inspirowany wierszem Vogon!
Thane Plummer

27

#define MULTILINE(...) #__VA_ARGS__
Zużywa wszystko między nawiasami.
Zastępuje dowolną liczbę następujących po sobie białych znaków jednym spacją.


1
Możesz dodać, \njeśli potrzebujesz nowych linii
Simon

Zauważ, że ` (and hence \ n ) is copied literally, but "jest konwertowany na \". Więc MULTILINE(1, "2" \3)daje "1, \"2\" \3".
Andreas Spindler

@AndreasSpindler Zarówno cytaty, jak i ukośniki odwrotne są usuwane przez (dodatkowe) ukośniki odwrotne, o ile pojawiają się w ciągu literału lub ciągu znaków. Nie jestem pewien, o co ci chodzi. Niedopasowanie cytatu (podwójnego lub pojedynczego) jest nielegalne, więc skurcze nie działają lub ich nieparzysta liczba, co jest prawdopodobnie największym minusem. W każdym razie +1. „Prawdziwi programiści” zawsze używają skurczów w parach, bez interweniujących znaków nowej linii, więc pojedyncze cytaty są zrównoważone.
Potatoswatter

Chodzi o to, że napisał „zużywa wszystko między nawiasami”.
Andreas Spindler

25

Prawdopodobnie wygodnym sposobem wprowadzania ciągów wieloliniowych jest użycie makr. Działa to tylko wtedy, gdy cytaty i nawiasy są zrównoważone i nie zawiera przecinków „najwyższego poziomu”:

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

Skompilowany z gcc 4.6 lub g ++ 4.6, daje to: [[Using this trick(,) you don't need to use quotes. Though newlines and multiple white spaces will be replaced by a single whitespace.]]

Zauważ, że ,nie może być w ciągu, chyba że jest zawarty w nawiasie lub cudzysłowie. Pojedyncze cudzysłowy są możliwe, ale tworzą ostrzeżenia kompilatora.

Edycja: Jak wspomniano w komentarzach, #define MULTI_LINE_STRING(...) #__VA_ARGS__umożliwia użycie ,.


W przypadku projektu, w którym chciałem dołączyć fragmenty kodu lua do c ++, skończyło się na napisaniu małego skryptu python, w którym wszedłem do łańcuchów wielowierszowych i pozwoliłem temu wygenerować plik źródłowy c ++.
bcmpinc

Idealne dla mnie, dodając ogromny, wieloliniowy ciąg liczb zmiennoprzecinkowych z pliku collada do testów jednostkowych. Nie chciałem umieszczać cytatów wszędzie, potrzebowałem rozwiązania kopiuj i wklej.
Soylent Graham

7
Możesz użyć, #define MULTILINE(...) #__VA_ARGS__jeśli chcesz, aby Twój ciąg zawierał przecinki.
Simon

2
Pamiętaj, że spowoduje to usunięcie większości dodatkowych Whitesapce (w tym wszystkich \ni \r), co jest w niektórych przypadkach przydatne, a dla innych śmiertelne.
BCS,


15

Możesz po prostu to zrobić:

const char *text = "This is my string it is "
     "very long";

Czym różni się od odpowiedzi @indind?
Sisir

1
@Sisir Wysłałem go 2 minuty przed odprężeniem.
Eric

Przepraszamy za tę część. Moje +1
Sisir

10

Ponieważ uncja doświadczenia jest warta mnóstwa teorii, wypróbowałem mały program testowy dla 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"
};

Skompiluj ten fragment, cpp -P -std=c++11 filenameaby go odtworzyć.

Sztuczka #__VA_ARGS__polega na tym, __VA_ARGS__że nie przetwarza separatora przecinków. Możesz więc przekazać go operatorowi naciągania. Wiodące i końcowe spacje są przycinane, a spacje (w tym nowe linie) między słowami są następnie kompresowane do pojedynczej spacji. Nawiasy muszą być zrównoważone. Myślę, że te niedociągnięcia wyjaśniają, dlaczego projektanci C ++ 11 #__VA_ARGS__dostrzegli potrzebę literałów surowych.


9

Aby wyjaśnić nieco komentarz @ emsr w odpowiedzi @ unwind, jeśli nie ma się szczęścia, aby mieć kompilator C ++ 11 (powiedzmy GCC 4.2.1), i chcemy osadzić znaki nowej linii w ciągu (albo char * lub ciąg klasy), można napisać coś takiego:

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

To bardzo oczywiste, prawda, ale krótki komentarz @ emsr nie wyskoczył na mnie, gdy przeczytałem to po raz pierwszy, więc musiałem to odkryć dla siebie. Mam nadzieję, że zaoszczędziłem komuś jeszcze kilka minut.


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

Dodaj wyjaśnienie do swojej odpowiedzi, a nie tylko fragmenty kodu
Geordie

-1

Opcja 1. Korzystając z biblioteki boost, możesz zadeklarować ciąg znaków jak poniżej

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

Opcja 2. Jeśli boost nie jest dostępny w twoim projekcie, możesz użyć std :: string_view () we współczesnym C ++.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.