¿Cómo dividir una cadena literal en varias líneas en C / Objective-C?

321

Tengo una consulta sqlite bastante larga:

const char *sql_query = "SELECT statuses.word_id FROM lang1_words, statuses WHERE statuses.word_id = lang1_words.word_id ORDER BY lang1_words.word ASC";

¿Cómo puedo dividirlo en varias líneas para que sea más fácil de leer? Si hago lo siguiente:

const char *sql_query = "SELECT word_id
                        FROM table1, table2
                        WHERE table2.word_id = table1.word_id
                        ORDER BY table1.word ASC";

Estoy recibiendo un error

¿Hay alguna manera de escribir consultas en varias líneas?

Ilya Suzdalnitski
fuente

Respuestas:

569

Hay dos formas de dividir cadenas en varias líneas:

Utilizando \

Todas las líneas en C se pueden dividir en varias líneas usando \.

Llanura C:

char *my_string = "Line 1 \
                   Line 2";

C objetivo:

NSString *my_string = @"Line1 \
                        Line2";

Mejor enfoque

Hay un mejor enfoque que funciona solo para cadenas.

Llanura C:

char *my_string = "Line 1 "
                  "Line 2";

C objetivo:

NSString *my_string = @"Line1 "
                       "Line2";    // the second @ is optional

El segundo enfoque es mejor porque no hay mucho espacio en blanco incluido. Sin embargo, para una consulta SQL, ambos son posibles.

NOTA: con un #define, debe agregar un '\' adicional para concatenar las dos cadenas:

Llanura C:

#define kMyString "Line 1"\
                  "Line 2"
Georg Schölly
fuente
22
Ambos son los mismos que en y C y C ++. Se prefiere la última solución porque la anterior incorpora una gran cantidad de espacio en blanco inútil en el programa que también se transmitirá al servidor de base de datos.
Alnitak
Te falta una @ al comienzo de la línea 2 en el mejor ejemplo de Objective-C.
Lawrence Johnston
¿Tiene un enlace a una especificación que documente la opcionalidad de la segunda @?
Heath Borders
@HeathBorders: No aquí, pero lo busqué cuando escribí la respuesta.
Georg Schölly
10
Otra ventaja del mejor enfoque es que puede poner // comentarios después de cada línea.
fishinear
110

Hay un truco que puedes hacer con el preprocesador.
Tiene el potencial negativo de colapsar los espacios en blanco, y podría ser confuso para las personas que leen el código.
Pero tiene el lado positivo de que no necesita escapar de los caracteres de comillas dentro de él.

#define QUOTE(...) #__VA_ARGS__
const char *sql_query = QUOTE(
    SELECT word_id
    FROM table1, table2
    WHERE table2.word_id = table1.word_id
    ORDER BY table1.word ASC
);

el preprocesador convierte esto en:

const char *sql_query = "SELECT word_id FROM table1, table2 WHERE table2.word_id = table1.word_id ORDER BY table1.word ASC";

He usado este truco cuando estaba escribiendo algunas pruebas unitarias que tenían grandes cadenas literales que contenían JSON. Significaba que no tenía que escapar de todos los caracteres de comillas ".

Nicholas Daley
fuente
55
¡Perfecto! Ahora solo necesito darle unos cientos de votos más y llegar a donde pertenece ...
Mike
Estaba reaccionando de la misma manera, pero esto no está exento de problemas. Acabo de intentar hacer un heredoc de esta manera con un carácter especial Unicode y obtuve un error acerca de que no se permiten caracteres no ASCII fuera de los literales.
philipkd
+1 pero para el registro estoy teniendo problemas con el compilador (MSVC) o el editor (QtCreator) no (re) compilando la expresión como debería en el cambio. Es como si no se detectaran cambios ... Golpear Reconstruir en lugar de Construir hace el truco.
Andreas
Gracias por esta información de Chicken Nugget. Hace exactamente lo que necesitaba hacer sin toda la basura extra.
FishGuy876
Desafortunadamente, esto no funciona si tiene comillas literales en la cadena. Bueno, de alguna manera funciona, ya que genera una advertencia. Pero mi base de código es -Werror ...
AnilRedshift
24

También puede ir a XCode -> Preferencias, seleccionar la pestaña Sangría y activar Ajuste de línea.

De esa manera, no tendrá que escribir nada extra, y funcionará para las cosas que ya escribió. :-)

Sin embargo, una cosa molesta es ...

if (you're long on indentation
    && short on windows) {
            then your code will
                end up squished
                     against th
                         e side
                             li
                              k
                              e

                              t
                              h
                              i
                              s
}
DenverCoder9
fuente
2
@YoYoYonnY Estoy de acuerdo, pero también lo aprecio. Me sorprende que este comentario no hubiera sido realmente posible como un comentario, de ahí el uso del formato de respuesta. Esto parece una limitación de S / O, que no puedes escribir comentarios particularmente ricos (que yo sepa).
Max von Hippel
24

Tengo este problema todo el tiempo, así que creé una pequeña herramienta para convertir texto en una cadena de Objective-C de varias líneas con escape:

http://multilineobjc.herokuapp.com/

Espero que esto te ahorre algo de tiempo.

Flaviu
fuente
1
gran herramienta! pregunta: ¿por qué escapas de '|'?
justadreamer
Buen punto. Lo cambié para que ya no escape "|". Gracias por hacérmelo saber.
Flaviu
Yo tenía la misma idea. Ojalá hubiera visto esto primero. Mi herramienta es: nsstringify.nateflink.com
Nate Flink
1
Gracias, me ahorró mucho tiempo!
djskinner
Intente usar el formato Clang (se integra con sus editores favoritos): clang.llvm.org/docs/ClangFormat.html
Ahmed Fasih
18

Extender la idea de Cita para Objective-C:

#define NSStringMultiline(...) [[NSString alloc] initWithCString:#__VA_ARGS__ encoding:NSUTF8StringEncoding]

NSString *sql = NSStringMultiline(
    SELECT name, age
    FROM users
    WHERE loggedin = true
);
Berik
fuente
3
#define NSStringMultiline(...) @#__VA_ARGS__debería funcionar también.
Nicholas Daley
Para cadenas mutables: #define NSStringMultiline(...) [[NSMutableString alloc] initWithCString:#__VA_ARGS__ encoding:NSUTF8StringEncoding]
rimsky
Para mí, la cadena resultante no tiene nuevas líneas.
rimsky
Las nuevas líneas escapadas se capturan correctamente (lo que no es tan conveniente o agradable).
rimsky
@rimsky, y creo que eso #define NSStringMultiline(...) [@#__VA_ARGS__ mutableCopy]también funciona para cadenas mutables.
Iulian Onofrei
5

Una solución más para la pila, cambie su archivo .m a .mm para que se convierta en Objective-C ++ y use literales en bruto C ++, como este:

const char *sql_query = R"(SELECT word_id
                           FROM table1, table2
                           WHERE table2.word_id = table1.word_id
                           ORDER BY table1.word ASC)";

Los literales sin procesar ignoran todo hasta la secuencia de terminación, que en el caso predeterminado es paréntesis-cita.

Si la secuencia entre comillas y paréntesis tiene que aparecer en algún lugar de la cadena, también puede especificar fácilmente un delimitador personalizado, como este:

const char *sql_query = R"T3RM!N8(
                                  SELECT word_id
                                  FROM table1, table2
                                  WHERE table2.word_id = table1.word_id
                                  ORDER BY table1.word ASC
                         )T3RM!N8";
John Stephen
fuente
También descubrí que GCC agrega literales de cadena
sin formato
3

También puedes hacer:

NSString * query = @"SELECT * FROM foo "
                   @"WHERE "
                     @"bar = 42 "
                     @"AND baz = datetime() "
                   @"ORDER BY fizbit ASC";
Dave DeLong
fuente
2

GCC agrega literales de cadena cruda multilínea C ++ como una extensión C

C ++ 11 tiene literales de cadena sin procesar como se menciona en: https://stackoverflow.com/a/44337236/895245

Sin embargo, GCC también los agrega como una extensión C, solo tiene que usarlos en -std=gnu99lugar de -std=c99. P.ej:

C Principal

#include <assert.h>
#include <string.h>

int main(void) {
    assert(strcmp(R"(
a
b
)", "\na\nb\n") == 0);
}

Compilar y ejecutar:

gcc -o main -pedantic -std=gnu99 -Wall -Wextra main.c
./main

Esto se puede usar, por ejemplo, para insertar un ensamblaje en línea multilínea en el código C: ¿Cómo escribir un código de ensamblaje en línea multilínea en GCC C ++?

Ahora solo tiene que recostarse y esperar a que se estandarice en C20XY.

Se le preguntó a C ++ en: literal de cadena multilínea C ++

Probado en Ubuntu 16.04, GCC 6.4.0, binutils 2.26.1.

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
fuente
0

Una alternativa es utilizar cualquier herramienta para eliminar saltos de línea. Escriba su cadena usando cualquier editor de texto, una vez que haya terminado, pegue su texto aquí y cópielo nuevamente en xcode.

OUBERGHOUZ MOHAMED
fuente
1
No hay realmente una solución a largo plazo. ¿Qué pasa si tiene que cambiarlo más tarde? Get es molesto rápido, mejor usar las técnicas de líneas múltiples ya mencionadas y formatearlo directamente en el archivo.
Schwarzie2478