Comment diviser un littéral de chaîne sur plusieurs lignes en C / Objective-C?

321

J'ai une assez longue requête sqlite:

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

Comment le diviser en plusieurs lignes pour en faciliter la lecture? Si je fais ce qui suit:

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

Je reçois une erreur.

Existe-t-il un moyen d'écrire des requêtes sur plusieurs lignes?

Ilya Suzdalnitski
la source

Réponses:

569

Il existe deux façons de fractionner des chaînes sur plusieurs lignes:

En utilisant \

Toutes les lignes en C peuvent être divisées en plusieurs lignes à l'aide de \.

Plaine C:

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

Objectif c:

NSString *my_string = @"Line1 \
                        Line2";

Meilleure approche

Il existe une meilleure approche qui fonctionne uniquement pour les chaînes.

Plaine C:

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

Objectif c:

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

La deuxième approche est meilleure, car il n'y a pas beaucoup d'espace blanc inclus. Cependant, pour une requête SQL, les deux sont possibles.

REMARQUE: avec un #define, vous devez ajouter un «\» supplémentaire pour concaténer les deux chaînes:

Plaine C:

#define kMyString "Line 1"\
                  "Line 2"
Georg Schölly
la source
22
Les deux sont les mêmes que dans et C et C ++. Cette dernière solution est préférée car l'ancienne incorpore beaucoup d'espace blanc inutile dans le programme qui sera également transmis au serveur DB.
Alnitak
Vous manquez un @ au début de la ligne 2 dans le meilleur exemple Objective-C.
Lawrence Johnston
Avez-vous un lien vers une spécification documentant le caractère facultatif de la seconde @?
Heath Borders
@HeathBorders: Pas ici, mais je l'ai recherché quand j'ai écrit la réponse.
Georg Schölly
10
Un autre avantage de la meilleure approche est que vous pouvez mettre // des commentaires après chaque ligne.
fishinear
110

Il y a une astuce que vous pouvez faire avec le pré-processeur.
Il présente les inconvénients potentiels de l'effondrement de l'espace blanc et pourrait être déroutant pour les personnes lisant le code.
Mais, il a l'avantage que vous n'avez pas besoin d'échapper aux guillemets à l'intérieur.

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

le préprocesseur transforme cela en:

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

J'ai utilisé cette astuce lorsque j'écrivais des tests unitaires contenant de grandes chaînes littérales contenant JSON. Cela signifiait que je n'avais pas à échapper à chaque caractère de citation \ ".

Nicholas Daley
la source
5
Parfait! Maintenant, j'ai juste besoin de donner quelques centaines de votes positifs supplémentaires, et de l'amener là où il appartient ...
Mike
Je réagissais de la même manière, mais ce n'est pas sans problème. J'ai juste essayé de faire un hérédoc de cette façon avec un caractère Unicode spécial et j'ai eu une erreur sur les caractères non ASCII non autorisés en dehors des littéraux.
philipkd
+1 mais pour mémoire, j'ai des problèmes avec le compilateur (MSVC) ou l'éditeur (QtCreator) qui ne (re) compile pas l'expression comme il se doit en cas de changement. C'est comme si le changement n'était pas détecté ... Frapper Rebuild au lieu de Build fait l'affaire.
Andreas
Merci pour cette pépite de poulet d'information. Il fait exactement ce que je devais faire sans tous les déchets supplémentaires.
FishGuy876
Cela ne fonctionne malheureusement pas si vous avez des guillemets littéraux dans la chaîne. Eh bien, cela fonctionne en quelque sorte, car il génère un avertissement. Mais ma base de code est -Werror ...
AnilRedshift
24

Vous pouvez également aller dans XCode -> Préférences, sélectionner l'onglet Retrait et activer le retour à la ligne.

De cette façon, vous n'aurez rien à taper de plus, et cela fonctionnera pour les choses que vous avez déjà écrites. :-)

Une chose ennuyeuse est cependant ...

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
la source
2
@YoYoYonnY Je suis d'accord, mais je l'apprécie également. Il me semble que ce commentaire n'aurait pas été vraiment possible en tant que commentaire, d'où l'utilisation du format de réponse. Cela semble être une limitation de S / O, que vous ne pouvez pas écrire de commentaires particulièrement riches (pour autant que je sache).
Max von Hippel
24

J'ai ce problème tout le temps, j'ai donc créé un petit outil pour convertir le texte en une chaîne Objective-C multi-lignes échappée:

http://multilineobjc.herokuapp.com/

J'espère que cela vous fera gagner du temps.

Flaviu
la source
1
excellent outil! question: pourquoi échappez-vous à "|"?
justadreamer
Bon point. Je l'ai changé pour ne plus échapper à "|". Merci de me le faire savoir.
Flaviu
J'ai eu la même idée. J'aurais aimé voir ça en premier. Mon outil est: nsstringify.nateflink.com
Nate Flink
1
Merci, ça m'a fait gagner beaucoup de temps!
djskinner
Essayez d'utiliser le format Clang (s'intègre à vos éditeurs préférés): clang.llvm.org/docs/ClangFormat.html
Ahmed Fasih
18

Extension de l' idée de devis pour Objective-C:

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

NSString *sql = NSStringMultiline(
    SELECT name, age
    FROM users
    WHERE loggedin = true
);
Berik
la source
3
#define NSStringMultiline(...) @#__VA_ARGS__devrait aussi fonctionner.
Nicholas Daley
Pour les cordes mutables: #define NSStringMultiline(...) [[NSMutableString alloc] initWithCString:#__VA_ARGS__ encoding:NSUTF8StringEncoding]
rimsky
Pour moi, la chaîne résultante n'a pas de nouvelles lignes.
rimsky
Les retours à la ligne échappés sont capturés correctement (ce qui n'est pas si pratique ou agréable).
rimsky
@rimsky, et je pense que cela #define NSStringMultiline(...) [@#__VA_ARGS__ mutableCopy]fonctionne également pour les chaînes mutables.
Iulian Onofrei
5

Une autre solution pour la pile, changez votre fichier .m en .mm pour qu'il devienne Objective-C ++ et utilisez des littéraux bruts C ++, comme ceci:

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

Les littéraux bruts ignorent tout jusqu'à la séquence de terminaison, qui dans le cas par défaut est la citation entre parenthèses.

Si la séquence entre guillemets parenthèses doit apparaître quelque part dans la chaîne, vous pouvez également spécifier facilement un délimiteur personnalisé, comme ceci:

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
la source
J'ai également constaté que GCC ajoute des littéraux de chaîne brute C ++ en tant qu'extension au langage C: stackoverflow.com/questions/797318/…
Ciro Santilli 法轮功 冠状 病 六四 事件 法轮功
3

Vous pouvez également faire:

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

GCC ajoute des littéraux de chaîne brute multilignes C ++ en tant qu'extension C

C ++ 11 a des littéraux de chaîne bruts comme mentionné sur: https://stackoverflow.com/a/44337236/895245

Cependant, GCC les ajoute également en tant qu'extension C, il vous suffit de les utiliser à la -std=gnu99place de -std=c99. Par exemple:

principal c

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

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

Compiler et exécuter:

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

Cela peut être utilisé par exemple pour insérer un assemblage en ligne multiligne dans du code C: Comment écrire du code d'assemblage en ligne multiligne dans GCC C ++?

Il ne vous reste plus qu'à vous détendre et à attendre qu'il soit normalisé sur C20XY.

C ++ a été demandé à: C ++ multiline string literal

Testé sur Ubuntu 16.04, GCC 6.4.0, binutils 2.26.1.

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

Une alternative consiste à utiliser n'importe quel outil pour supprimer les sauts de ligne. Écrivez votre chaîne en utilisant n'importe quel éditeur de texte, une fois que vous avez terminé, collez votre texte ici et copiez-le à nouveau dans xcode.

OUBERGHOUZ MOHAMED
la source
1
Pas vraiment une solution à long terme. Et si vous deviez le changer à nouveau plus tard. Get est agaçant rapidement, mieux vaut utiliser les technologies multi-lignes déjà mentionnées et les formater directement dans le fichier.
Schwarzie2478