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.

rlbond
źródło
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.

rozwijać
źródło
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.

emsr
źródło
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ą.

Zlatan Stanojević
źródło
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 ,.

bcmpinc
źródło
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,
17

Możesz także to zrobić:

const char *longString = R""""(
This is 
a very 
long 
string
)"""";
Raydelto Hernandez
źródło
2
dzięki, to jest świetne, działa nawet w C. oczywiście, char longString[] = R""""( This is a very long string )""""; też działa dla mnie.
struggling_learner
2
Czy to zaczyna i kończy ciąg z nową linią?
Tim MB
1
To dosłowny ciąg znaków . Dostępne od C ++ 11.
Mikolasan
15

Możesz po prostu to zrobić:

const char *text = "This is my string it is "
     "very long";
Eric
źródło
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.

Andreas Spindler
źródło
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.

CXJ
źródło
-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";
użytkownik3635122
źródło
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 ++.

piyu2cool
źródło