Literal de cadena multilínea C ++

416

¿Hay alguna manera de tener texto plano de varias líneas, literales constantes en C ++, a la Perl? Tal vez algún truco de análisis con #includeun archivo? No puedo pensar en uno, pero chico, eso sería bueno. Sé que estará en C ++ 0x.

rlbond
fuente
1
En general, no desea incrustar literales de cadena en el código. Para I18N y L10N, es preferible colocar literales de cadena en un archivo de configuración que se carga en tiempo de ejecución.
Martin York
46
Hay suficientes casos donde poner literales de cadena en el código no es un problema: si la cadena no se usa para representarla ante el usuario; es decir: sentencias de SQL, nombres de archivo, nombres de clave de registro, líneas de comando a ejecutar, ...
mmmmmmmm
2
@ Martin: Sin embargo, aún puede ser útil saberlo. Lo he hecho para romper expresiones regulares complejas, por ejemplo.
Boojum

Respuestas:

591

Especie de. Lo más fácil es usar el hecho de que el compilador concatena literales de cadena adyacentes:

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

La sangría no importa, ya que no está dentro de las comillas.

También puede hacerlo, siempre y cuando tenga cuidado de escapar de la nueva línea incrustada. De lo contrario, como lo hizo mi primera respuesta, no se compilará:

const char * text2 =
  "Aquí, por otro lado, me he vuelto loco \
y realmente dejo que el literal abarque varias líneas, \
sin molestarse en citar cada línea
contenido. Esto funciona, pero no puede sangrar ";

Nuevamente, tenga en cuenta esas barras invertidas al final de cada línea, deben estar inmediatamente antes de que la línea termine, están escapando de la nueva línea en la fuente, de modo que todo actúe como si la nueva línea no estuviera allí. No obtienes nuevas líneas en la cadena en los lugares donde tenías barras invertidas. Con este formulario, obviamente no puede sangrar el texto, ya que la sangría pasaría a formar parte de la cadena y la confinaría con espacios aleatorios.

relajarse
fuente
3
Me han dicho en el pasado que la primera opción puede estar a la altura de la implementación, sin embargo, aún no he encontrado un compilador que no respete esa sintaxis.
Jason Mock
28
@Jason: no era necesariamente una parte de los compiladores anteriores a C89, pero está definido en C89 y, por lo tanto, se admite esencialmente en todas partes.
Jonathan Leffler
44
Además, si realmente desea que la cadena se formatee en varias líneas en c ++ 98, simplemente sustituya \ n por el espacio de terminación en cada fragmento de cadena entre comillas. Los literales en bruto de C ++ 11 siguen siendo mis favoritos.
Emsr
3
@unwind Tenga en cuenta que la nueva línea al final de la línea de origen no forma parte de la cadena, solo se omite. Si desea una nueva línea como parte de la cadena, debe tener \ n \ al final de la línea.
hyde
2
Hay un error desagradable en Microsoft Visual Studio. Si usa barras invertidas al final de las líneas, sangra automáticamente el texto dentro de la cadena.
palota
409

En C ++ 11 tiene literales de cadena sin formato. Algo así como texto aquí en shells y lenguajes de script como Python y Perl y 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";

Todos los espacios y sangría y las nuevas líneas en la cadena se conservan.

También pueden ser utf-8 | 16 | 32 o wchar_t (con los prefijos habituales).

Debo señalar que la secuencia de escape, V0G0N, no es realmente necesaria aquí. Su presencia permitiría poner) "dentro de la cadena. En otras palabras, podría haber puesto

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

(tenga en cuenta las comillas adicionales) y la cadena anterior aún sería correcta. De lo contrario, podría haber usado

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

Todavía se necesitan los parens dentro de las comillas.

emsr
fuente
24
Esto es realmente lo que quiero, la capacidad de evitar comillas, barras invertidas-N, escapes y todavía aparecen nuevas líneas en la cadena real. Esto es útil para el código incrustado (por ejemplo, sombreadores o Lua). Desafortunadamente, no todos estamos usando C ++ - 0x todavía. :-(
mlepage
2
Estaba considerando esto para los scripts de SQL y Python integrados. Esperaba por tu bien si tal vez gcc lo dejaría pasar en modo C ++ 98 pero, por desgracia, no.
emsr
3
Estoy más acostumbrado a tocar y gcc. En este compilador, debe establecer un indicador para C ++ 0x o c ++ 11. Parece que en el sitio web de MS parece que todavía no tienen literales en bruto. Entiendo que MS lanzará nuevas actualizaciones del compilador más rápidamente a medida que se implementen las funciones de C ++. Busque el compilador de Visual C ++ CTP de noviembre de 2012 [ microsoft.com/en-us/download/details.aspx?id=35515] para obtener la última versión de sangrado.
Emsr
55
@rsethc Solo usa #if 0... #endifpara comentar bloques de código. Nidos también.
bobbogo
1
¡Inspirado en el poema de Vogon!
Thane Plummer
27

#define MULTILINE(...) #__VA_ARGS__
Consume todo entre paréntesis.
Reemplaza cualquier cantidad de caracteres de espacio en blanco consecutivos por un solo espacio.

Zlatan Stanojević
fuente
1
Puede agregar \nsi necesita nuevas líneas
Simon
Tenga en cuenta que ` (and hence \ n ) is copied literally, but "` se convierte en \". Por lo tanto, MULTILINE(1, "2" \3)rinde "1, \"2\" \3".
Andreas Spindler
@AndreasSpindler Las comillas y las barras invertidas se escapan por barras diagonales (adicionales) siempre que aparezcan dentro de una cadena o token literal de caracteres. No estoy seguro de cuál es tu punto. Es ilegal tener una cotización sin igual (doble o simple), por lo que las contracciones no funcionan, o un número impar de todas formas, lo que probablemente sea el mayor inconveniente. +1 de todos modos. Los "programadores reales" siempre usan contracciones en pares sin una nueva línea interpuesta, por lo que las comillas simples se equilibran.
Potatoswatter
El punto es que escribió "consume todo entre paréntesis".
Andreas Spindler
25

Una forma probablemente conveniente de ingresar cadenas de varias líneas es mediante el uso de macros. Esto solo funciona si las comillas y paréntesis están equilibrados y no contiene comas de 'nivel superior':

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

Compilado con gcc 4.6 o g ++ 4.6, esto produce: [[Using this trick(,) you don't need to use quotes. Though newlines and multiple white spaces will be replaced by a single whitespace.]]

Tenga en cuenta que ,no puede estar en la cadena, a menos que esté entre paréntesis o comillas. Las comillas simples son posibles, pero crean advertencias del compilador.

Editar: como se menciona en los comentarios, #define MULTI_LINE_STRING(...) #__VA_ARGS__permite el uso de ,.

bcmpinc
fuente
Para un proyecto en el que quería incluir algunos fragmentos de código lua en c ++, terminé escribiendo un pequeño script de python, en el que ingresé las cadenas multilínea, y dejé que eso generara un archivo fuente c ++.
bcmpinc
Perfecto para mí, agregando una enorme cadena de lista flotante de varias líneas de un archivo collada para pruebas unitarias. No me gustaba poner comillas en todas partes, necesitaba una solución de copiar y pegar.
Soylent Graham
77
Puede usar #define MULTILINE(...) #__VA_ARGS__si desea que su cadena contenga comas.
Simon
2
Tenga en cuenta que esto eliminará la mayoría de los whitesapce adicionales (incluidos todos \ny \r), lo que es útil para algunos casos y fatal para otros.
BCS
17

También puedes hacer esto:

const char *longString = R""""(
This is 
a very 
long 
string
)"""";
Raydelto Hernández
fuente
2
gracias, esto es genial, funciona incluso en C. obviamente, char longString[] = R""""( This is a very long string )""""; también funciona para mí.
fighting
2
¿Esto comienza y termina la cadena con una nueva línea?
Tim MB
1
Es un literal de cadena sin formato . Disponible desde C ++ 11.
Mikolasan
15

Puedes hacer esto:

const char *text = "This is my string it is "
     "very long";
Eric
fuente
¿Cómo es diferente a la respuesta de @ unwind?
Sisir
1
@Sisir Lo publiqué 2 minutos antes de que se desenrollara.
Eric
Disculpas por perder esa parte. Mi +1
Sisir
10

Dado que una onza de experiencia vale un montón de teoría, probé un pequeño programa de prueba para 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"
};

Compile este fragmento con cpp -P -std=c++11 filenamepara reproducir.

El truco detrás #__VA_ARGS__es que __VA_ARGS__no procesa el separador de coma. Para que pueda pasarlo al operador de encadenamiento. Los espacios iniciales y finales se recortan, y los espacios (incluidas las nuevas líneas) entre palabras se comprimen en un solo espacio. Los paréntesis deben ser equilibrados. Creo que estas deficiencias explican por qué los diseñadores de C ++ 11, a pesar de eso #__VA_ARGS__, vieron la necesidad de literales de cadena sin formato.

Andreas Spindler
fuente
9

Solo para dilucidar un poco sobre el comentario de @emsr en la respuesta de @windind, si uno no tiene la suerte de tener un compilador de C ++ 11 (por ejemplo, GCC 4.2.1), y quiere insertar las nuevas líneas en la cadena (ya sea char * o cadena de clase), se puede escribir algo como esto:

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

Muy obvio, cierto, pero el breve comentario de @emsr no me llamó la atención cuando lo leí la primera vez, así que tuve que descubrirlo por mí mismo. Con suerte, he salvado a otra persona unos minutos.

CXJ
fuente
-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";
usuario3635122
fuente
Agregue una explicación a su respuesta y no solo fragmentos de código
Geordie
-1

Opción 1. Usando la biblioteca de impulso, puede declarar la cadena de la siguiente manera

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

Opción 2. Si boost no está disponible en su proyecto, puede usar std :: string_view () en C ++ moderno.

piyu2cool
fuente