Jak podzielić literał ciąg na wiele wierszy w C / Objective-C?

320

Mam dość długie zapytanie 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";

Jak mogę podzielić go na kilka wierszy, aby ułatwić czytanie? Jeśli wykonam następujące czynności:

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

Pojawia się błąd.

Czy istnieje sposób pisania zapytań w wielu wierszach?

Ilya Suzdalnitski
źródło

Odpowiedzi:

568

Istnieją dwa sposoby podziału ciągów na wiele wierszy:

Za pomocą \

Wszystkie linie w C można podzielić na wiele linii za pomocą \.

Zwykły C:

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

Cel C:

NSString *my_string = @"Line1 \
                        Line2";

Lepsze podejście

Jest lepsze podejście, które działa tylko na łańcuchy.

Zwykły C:

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

Cel C:

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

Drugie podejście jest lepsze, ponieważ nie zawiera wielu białych znaków. W przypadku zapytania SQL oba są jednak możliwe.

UWAGA: W przypadku #define musisz dodać dodatkowy „\”, aby połączyć dwa ciągi:

Zwykły C:

#define kMyString "Line 1"\
                  "Line 2"
Georg Schölly
źródło
22
Oba są takie same jak w C oraz C ++. To drugie rozwiązanie jest preferowane, ponieważ poprzednie zawiera wiele niepotrzebnych białych znaków w programie, które zostaną również przesłane do serwera DB.
Alnitak
Brakuje znaku @ na początku wiersza 2 w lepszym przykładzie Celu-C.
Lawrence Johnston,
Czy masz link do specyfikacji dokumentującej opcjonalność drugiego @?
Heath Borders
@HeathBorders: Nie tutaj, ale sprawdziłem to, kiedy napisałem odpowiedź.
Georg Schölly
10
Kolejną zaletą lepszego podejścia jest możliwość wstawiania // komentarzy po każdej linii.
fishinear
110

Jest pewien sposób, który możesz zrobić z preprocesorem.
Ma potencjalnie negatywne strony, że zwinie białe znaki i może być mylące dla osób czytających kod.
Ma jednak tę zaletę, że nie musisz uciec od znaków cudzysłowu.

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

preprocesor przekształca to w:

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

Użyłem tej sztuczki, kiedy pisałem kilka testów jednostkowych, które miały duże dosłowne ciągi znaków zawierające JSON. Oznaczało to, że nie musiałem uciekać przed każdym znakiem cytatu \ ".

Nicholas Daley
źródło
5
Doskonały! Teraz muszę tylko podać kilkaset głosów pozytywnych i dostać to tam, gdzie należy ...
Mike
Reagowałem w ten sam sposób, ale nie bez problemów. Właśnie próbowałem zrobić heredoc w ten sposób ze specjalnym znakiem Unicode i dostałem błąd, że znaki spoza ASCII nie są dozwolone poza literałami.
philipkd
+1, ale dla rekordu mam problem z kompilatorem (MSVC) lub edytorem (QtCreator), który nie (ponownie) kompiluje wyrażenia tak, jak powinien przy zmianie. To tak, jakby zmiana nie została wykryta ... Uderzenie w Przebudowanie zamiast Kompilacji rozwiązuje problem.
Andreas,
Dziękuję za ten samorodek kurczaka z informacji. Robi dokładnie to, co musiałem zrobić bez wszystkich dodatkowych śmieci.
FishGuy876
To niestety nie działa, jeśli w łańcuchu znajdują się dosłowne cudzysłowy. Cóż, to działa, ponieważ generuje ostrzeżenie. Ale moją bazą kodów jest -Werror ...
AnilRedshift
24

Możesz także przejść do XCode -> Preferencje, wybrać kartę Wcięcie i włączyć Zawijanie linii.

W ten sposób nie będziesz musiał pisać nic więcej, a to zadziała dla rzeczy, które już napisałeś. :-)

Jedną z denerwujących rzeczy jest ...

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
źródło
2
@YoYoYonnY Zgadzam się, ale też to doceniam. Uderza mnie, że ten komentarz nie byłby naprawdę możliwy jako komentarz, stąd użycie formatu odpowiedzi. To wydaje się ograniczeniem S / O, że nie możesz pisać szczególnie bogatych komentarzy (o ile mi wiadomo).
Max von Hippel
24

Cały czas mam ten problem, więc stworzyłem małe narzędzie do konwersji tekstu na wielowierszowy łańcuch C celu:

http://multilineobjc.herokuapp.com/

Mam nadzieję, że zaoszczędzi ci to trochę czasu.

Flaviu
źródło
1
świetne narzędzie! pytanie: dlaczego uciekasz od „|”?
justadreamer
Słuszna uwaga. Zmieniłem go, aby nie uciekał już „|”. Dzięki, że dałeś mi znać.
Flaviu
Miałem ten sam pomysł. Szkoda, że ​​nie widziałbym tego pierwszy. Moje narzędzie to: nsstringify.nateflink.com
Nate Flink
1
Dzięki, zaoszczędziłeś mi dużo czasu!
djskinner
Spróbuj użyć formatu Clanga (integruje się z twoimi ulubionymi edytorami): clang.llvm.org/docs/ClangFormat.html
Ahmed Fasih
18

Rozszerzanie Quote pomysł na Objective-C:

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

NSString *sql = NSStringMultiline(
    SELECT name, age
    FROM users
    WHERE loggedin = true
);
Berik
źródło
3
#define NSStringMultiline(...) @#__VA_ARGS__też powinien działać.
Nicholas Daley
W przypadku łańcuchów zmiennych: #define NSStringMultiline(...) [[NSMutableString alloc] initWithCString:#__VA_ARGS__ encoding:NSUTF8StringEncoding]
rimsky
Dla mnie wynikowy ciąg nie ma nowych linii.
rimsky
Nowe znaki nowej linii są przechwytywane poprawnie (co nie jest prawie tak wygodne ani przyjemne).
rimsky
@rimsky, i myślę, że to #define NSStringMultiline(...) [@#__VA_ARGS__ mutableCopy]również działa na zmienne ciągi znaków.
Iulian Onofrei
5

Jeszcze jedno rozwiązanie dla stosu, zmień plik .m na .mm, aby stał się Objective-C ++ i użyj surowych literałów C ++, jak poniżej:

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

Surowe literały ignorują wszystko aż do sekwencji kończącej, która w domyślnym przypadku to nawias-cytat.

Jeśli sekwencja nawias-cudzysłów musi się gdzieś pojawiać w ciągu, możesz łatwo określić niestandardowy separator, na przykład:

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
źródło
Przekonałem się również, że GCC dodaje
dosłowne ciągi
3

Możesz także:

NSString * query = @"SELECT * FROM foo "
                   @"WHERE "
                     @"bar = 42 "
                     @"AND baz = datetime() "
                   @"ORDER BY fizbit ASC";
Dave DeLong
źródło
2

GCC dodaje C ++ multiline literały ciągów surowych jako rozszerzenie C.

C ++ 11 ma surowe literały łańcuchowe, jak wspomniano na stronie : https://stackoverflow.com/a/44337236/895245

Jednak GCC dodaje je również jako rozszerzenie C, po prostu musisz użyć -std=gnu99zamiast -std=c99. Na przykład:

main.c

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

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

Skompiluj i uruchom:

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

Można tego użyć na przykład do wstawienia wielowierszowego zestawu wbudowanego do kodu C: Jak napisać wielowierszowy kod zestawu wbudowanego w GCC C ++?

Teraz wystarczy się położyć i poczekać, aż zostanie znormalizowany w C20XY.

C ++ został zapytany na: C ++ literał ciągu wieloliniowego

Testowane na Ubuntu 16.04, GCC 6.4.0, binutils 2.26.1.

Ciro Santilli
źródło
0

Alternatywą jest użycie dowolnego narzędzia do usuwania podziałów linii. Napisz ciąg za pomocą dowolnego edytora tekstu, a po zakończeniu wklej tutaj tekst i skopiuj go ponownie w xcode.

OUBERGHOUZ MOHAMED
źródło
1
Naprawdę nie ma długoterminowego rozwiązania. Co jeśli będziesz musiał to zmienić później. Szybko irytuj, lepiej skorzystać z wyżej wymienionych technik wieloliniowych i sformatować je bezpośrednio w pliku.
Schwarzie2478