Wieloliniowy ciąg literału w C #


1046

Czy istnieje prosty sposób na utworzenie literału ciągu wielowierszowego w języku C #?

Oto co mam teraz:

string query = "SELECT foo, bar"
+ " FROM table"
+ " WHERE id = 42";

Wiem, że PHP ma

<<<BLOCK

BLOCK;

Czy C # ma coś podobnego?


1
W twoim przykładzie nie ma podziału linii. Chcesz je?
weiqure

8
Nie. Chciałem tylko wielu linii ze względu na widoczność / czystość kodu.
Chet,

6
W takim przypadku dosłowne ciągi zawierają podział wiersza. Możesz użyć @ „...”. Zamień (Environment.NewLine, „”), jeśli chcesz.
weiqure

9
Należy rozważyć powiązanie 42parametru jako parametru, zwłaszcza jeśli pochodzi on z danych wejściowych użytkownika, aby uniknąć wstrzyknięcia SQL.
Jens Mühlenhoff

@ JensMühlenhoff jak ktoś wstrzyknie zakodowany ciąg, który jest zdefiniowany w kodzie?
Pan Boy

Odpowiedzi:


1587

Możesz użyć @symbolu przed a, stringaby utworzyć dosłowny ciąg literału :

string query = @"SELECT foo, bar
FROM table
WHERE id = 42";

Nie musisz także uciekać przed znakami specjalnymi , korzystając z tej metody, z wyjątkiem podwójnych cudzysłowów, jak pokazano w odpowiedzi Jona Skeeta.


43
W każdym razie jest to dosłowny ciąg znaków - jest to dosłowny ciąg znaków ze znakiem @.
Jon Skeet,

95
jeśli ciąg zawiera podwójne cudzysłowy ( "), można uciec im tak:«»(to dwa znaki cudzysłowu)
Muad'Dib

8
Czy istnieje sposób na wykonanie powyższego bez tworzenia nowych linii? Mam naprawdę długi fragment tekstu, który chciałbym zobaczyć zawinięty w IDE bez konieczności używania znaku plus („cześć” + „tam”).
noelicus,

2
@ noelicus - nie bardzo, ale można to obejść, stosując powyższą technikę weiqure:@"my string".Replace(Environment.NewLine, "")
John Rasch,

8
@afsharm Możesz użyć $ @ „text”
Patrick McDonald

566

W języku C # nazywa się to dosłowne dosłowne ciągi literalne , a kwestią jest umieszczenie @ przed literałem. Pozwala to nie tylko na wiele linii, ale także wyłącza ucieczkę. Na przykład możesz wykonać:

string query = @"SELECT foo, bar
FROM table
WHERE name = 'a\b'";

Obejmuje to jednak podział wiersza (przy użyciu dowolnego podziału wiersza, jaki ma je źródło) w ciągu. W przypadku SQL jest to nie tylko nieszkodliwe, ale prawdopodobnie poprawia czytelność w dowolnym miejscu łańcucha - ale w innych miejscach może nie być wymagane, w takim przypadku albo nie musisz używać dosłownego ciągu literowego o wielu wierszach, lub usuń je z powstałego ciągu.

Jedyną różnicą jest to, że jeśli chcesz podwójnego cudzysłowu, musisz dodać dodatkowy symbol podwójnego cudzysłowu:

string quote = @"Jon said, ""This will work,"" - and it did!";

2
Ta odpowiedź jest niepoprawna; wprowadza nowe linie, których OP nie chce.
TamaMcGlinn

@TamaMcGlinn: Dodam coś do odpowiedzi na ten temat - nie było jasne, kiedy OP napisało pytanie.
Jon Skeet

105

Problem z używaniem literału ciągów, który znajduję, polega na tym, że może sprawić, że twój kod będzie wyglądał „ dziwnie ”, ponieważ aby nie uzyskać spacji w samym ciągu, należy go całkowicie wyrównać:

    var someString = @"The
quick
brown
fox...";

Fuj

Tak więc rozwiązaniem, które lubię używać, dzięki czemu wszystko ładnie dopasowuje się do reszty kodu:

var someString = String.Join(
    Environment.NewLine,
    "The",
    "quick",
    "brown",
    "fox...");

I oczywiście, jeśli chcesz po prostu logicznie podzielone wiersze instrukcji SQL jak jesteś i w rzeczywistości nie potrzebują nowej linii, zawsze można po prostu zastąpić Environment.NewLineza " ".


2
Dużo czystsze, dzięki. String.Concat działa również podobnie i nie wymaga separatora.
Seth

Dzięki. Podoba mi się String.Join z ogranicznikiem „” dla SQL, ponieważ pozwala on na wcięcia / dopasowywanie paren i unika konieczności dodawania początkowej spacji.
Rob na TVSeries.com

7
Choć brzydka, pierwsza wersja nie wymaga kodu do uruchomienia . Druga opcja oczywiście ma narzut związany z wykonywaniem w celu połączenia poszczególnych ciągów.
Gone Coding

@GoneCoding, jesteś tego pewien? Kompilator może zoptymalizować konkatenację.
William Jockusch

@WilliamJockusch: generalnie wywołania funkcji innych niż operator (jak łączenie) pozostają nietknięte i nie są zoptymalizowane. Najlepiej sprawdź skompilowany kod, ale położyłbym pieniądze, że połączenie nie jest zoptymalizowane.
Gone Coding

104

Inną sprawą, na którą trzeba uważać, jest użycie literałów łańcuchowych w łańcuchu znaków. W takim przypadku musisz uciec nawiasom klamrowym / nawiasom klamrowym „{” i „}”.

// this would give a format exception
string.Format(@"<script> function test(x) 
      { return x * {0} } </script>", aMagicValue)
// this contrived example would work
string.Format(@"<script> function test(x) 
      {{ return x * {0} }} </script>", aMagicValue)

14
A jaką to robi różnicę? Z lub bez „@” musisz podwoić „{{”, aby otrzymać „{” jako znak do wydrukowania, jest to format String.Format, a nie treść ciągu.
greenoldman

12
Jest to godna uwagi gotcha dla osób, które chcą wstawić kod JavaScript w łańcuch, co może być wykonywane częściej w dosłownym dosłownym łańcuchu niż w zwykłych ciągach.
Ed Brannin

2
W nowej wersji C # 6.0 można używać indeksowanego operatora właściwości wraz z dosłownym ciągiem literalnym (np. $ @ „Wartość to {this.Value}”;)
Heliac

2
@Heliac Myślę, że masz na myśli interpolowane ciągi znaków, które również mogą być dosłowne w tej składni. var query = $ @ "wybierz foo, pasek z tabeli gdzie id = {id}";
Brianary

102

Na marginesie, w C # 6.0 możesz teraz łączyć interpolowane ciągi z dosłownym dosłownym ciągiem:

string camlCondition = $@"
<Where>
    <Contains>
        <FieldRef Name='Resource'/>
        <Value Type='Text'>{(string)parameter}</Value>
    </Contains>
</Where>";

10
Fajnie, do tej pory o tym nie wiedziałem. Jeśli ktoś jest zainteresowany: $ thingy nazywa się „Interpolated Strings” i możesz przeczytać o tym szczegółowo tutaj: msdn.microsoft.com/en-us/library/dn961160.aspx
Hauke ​​P.

tx, poprawiono sformułowanie
Heliac

1
Zakładam, że dosłowne nawiasy klamrowe muszą zostać podwojone, np. $@"{{example literal text { fooString }. }}" Może to trochę pomylić, ponieważ Angular, React i Vue.js używają przeciwnej konwencji.
Patrick Szalapski

58

Dlaczego ludzie mylą ciągi z literałami ciągu? Przyjęta odpowiedź jest świetną odpowiedzią na inne pytanie; nie do tego.

Wiem, że to stary temat, ale przyszedłem tutaj z prawdopodobnie tym samym pytaniem co OP, i frustrujące jest obserwowanie, jak ludzie wciąż go błędnie interpretują. A może źle to rozumiem, nie wiem.

Z grubsza mówiąc, ciąg znaków to obszar pamięci komputera, który podczas wykonywania programu zawiera sekwencję bajtów, które można odwzorować na znaki tekstowe. Z drugiej strony, literał ciągu znaków jest fragmentem kodu źródłowego, który nie został jeszcze skompilowany i reprezentuje wartość użytą do zainicjowania ciągu znaków później podczas wykonywania programu, w którym się pojawia.

W języku C # instrukcja ...

 string query = "SELECT foo, bar"
 + " FROM table"
 + " WHERE id = 42";

... nie produkuje trzywierszowego sznurka, ale jedną wkładkę; konkatenacja trzech łańcuchów (każdy zainicjowany z innego literału), z których żaden nie zawiera modyfikatora nowej linii.

To, o co OP wydaje się pytać - a przynajmniej to, o co pytałbym tymi słowami - nie polega na tym, jak wprowadzić w skompilowanym ciągu znaków podział wierszy naśladujący te znajdujące się w kodzie źródłowym, ale na jak długo rozdzielić dla zachowania przejrzystości , pojedynczy wiersz tekstu w kodzie źródłowym bez wprowadzania przerw w skompilowanym ciągu. I nie wymagając dłuższego czasu wykonywania, spędziłem dołączając do wielu podciągów pochodzących z kodu źródłowego. Podobnie jak końcowe ukośniki odwrotne w literale ciągu wielowierszowego w javascript lub C ++.

Sugerowanie użycia dosłownych ciągów, nieistotnych StringBuilders, s, String.Joina nawet funkcji zagnieżdżonych z odwracaniem ciągów, a co nie, sprawia, że ​​myślę, że ludzie tak naprawdę nie rozumieją pytania. A może nie rozumiem tego.

O ile mi wiadomo, C # nie ma (przynajmniej w wersji paleolitycznej, której wciąż używam, z poprzedniej dekady) funkcji czystego generowania wieloliniowych literałów łańcuchowych, które można rozwiązać podczas kompilacji, a nie wykonywania.

Być może obecne wersje to obsługują, ale pomyślałem, że podzielę różnicę, którą dostrzegam między łańcuchami i literałami łańcucha.

AKTUALIZACJA:

(Od komentarza MeowCat2012) Możesz. Podejście „+” OP jest najlepsze. Zgodnie ze specyfikacją gwarantowana jest optymalizacja: http://stackoverflow.com/a/288802/9399618


1
Zamieszanie, jakie niektórzy (w tym ja) mogliby mieć patrząc na pierwotne pytanie, polega na tym, że format tutaj-doc (shell, php, perl, ...) zawiera znaki nowej linii. Więc jeśli PO porównuje się do heredoc PHP, to włączenie nowych linii nie powinno być problemem.
Tanktalus

Dokładnie. O ile wiem, twoja odpowiedź „nie, nie możesz tego zrobić w C #” jest poprawna.
TamaMcGlinn

Powiedziano mi, że w Javie taki połączony ciąg stanie się literałem pojedynczego ciągu w skompilowanym kodzie bajtowym. Może dot Net też to robi?
Meow Cat 2012

2
Możesz. Podejście „+” OP jest najlepsze. Zgodnie ze specyfikacją optymalizacja jest gwarantowana: stackoverflow.com/a/288802/9399618 Nadal jesteś jednym z niewielu, którzy rozumieją to pytanie. Czy byłoby możliwe usunięcie lub złożenie potencjalnie wprowadzających w błąd, ale zaakceptowanych odpowiedzi?
Meow Cat 2012

1
Dzięki za podpowiedź, @ MeowCat2012. Patrząc na jakiś zdekompilowany kod, wydaje się, że rzeczywiście tak jest.
Carvo Loco

12

Nie widziałem tego, więc opublikuję go tutaj (jeśli chcesz przekazać ciąg znaków, możesz to zrobić). Pomysł polega na tym, że możesz podzielić ciąg na wiele wierszy i dodać własną treść (również na wielu liniach) w dowolny sposób. Tutaj „tableName” można przekazać do ciągu.

    private string createTableQuery = "";

    void createTable(string tableName)
    {

         createTableQuery = @"CREATE TABLE IF NOT EXISTS
                ["+ tableName  + @"] (
               [ID] INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, 
               [Key] NVARCHAR(2048)  NULL, 
               [Value] VARCHAR(2048)  NULL
                                )";
    }

10
dość niebezpieczne, powiedziałbym: łatwo zrobić komuś zastrzyk w ten sposób.
Kai

4
Jest w porządku, o ile wiesz, że wszystkie twoje zmienne (jeśli istnieją!) W ciągu zapytania pochodzą ze stałych kodu lub innego bezpiecznego źródła - np. createTable("MyTable"); W każdym razie pytanie OP dotyczyło sposobu wprowadzania literałów ciągu wielowierszowego bezpośrednio w kodzie , a nie jak konstruować zapytania do bazy danych per se. :)
dbeachy1

2
powinien prawdopodobnie użyć konstruktora ciągów, szczególnie jeśli liczba plusów (+) jest duża.
gldraphael

8

Możesz użyć @ i „” .

        string sourse = @"{
        ""items"":[
        {
            ""itemId"":0,
            ""name"":""item0""
        },
        {
            ""itemId"":1,
            ""name"":""item1""
        }
        ]
    }";

2
Powinieneś wyjaśnić, co robi każdy, ponieważ @ oznacza dosłowny ciąg, a „” oznacza unikanie podwójnych cudzysłowów.
AFract

4

Tak, możesz podzielić ciąg na wiele wierszy bez wprowadzania nowych wierszy do rzeczywistego ciągu, ale nie jest ładny:

string s = $@"This string{
string.Empty} contains no newlines{
string.Empty} even though it is spread onto{
string.Empty} multiple lines.";

Sztuką jest wprowadzenie kodu, który ewaluuje do pustego, a kod ten może zawierać znaki nowej linii bez wpływu na wynik. Dostosowałem to podejście z tej odpowiedzi do podobnego pytania.

Wygląda na to, że istnieje pewne zamieszanie co do pytania, ale istnieją dwie wskazówki, że chcemy tutaj dosłowny ciąg znaków nie zawierający żadnych znaków nowego wiersza, którego definicja obejmuje wiele wierszy. (w komentarzach tak mówi, a „oto co mam” pokazuje kod, który nie tworzy łańcucha z nowymi liniami)

Ten test jednostkowy pokazuje zamiar:

    [TestMethod]
    public void StringLiteralDoesNotContainSpaces()
    {
        string query = "hi"
                     + "there";
        Assert.AreEqual("hithere", query);
    }

Zmień powyższą definicję zapytania, tak aby był to jeden literał łańcuchowy, zamiast łączenia dwóch literałów łańcuchowych, które kompilator może zoptymalizować w kompilatorze.

Podejście C ++ polega na kończeniu każdego wiersza odwrotnym ukośnikiem, co powoduje, że znak nowej linii jest zastępowany i nie pojawia się na wyjściu. Niestety nadal istnieje problem polegający na tym, że każda linia po pierwszej musi być wyrównana, aby nie dodawać dodatkowych białych znaków do wyniku.

Jest tylko jedna opcja, która nie polega na optymalizacjach kompilatora, które mogą się nie zdarzyć, czyli umieszczenie definicji w jednym wierszu. Jeśli chcesz polegać na optymalizacjach kompilatora, + już masz świetny; nie musisz wyrównywać łańcucha w lewo, w wyniku nie otrzymujesz nowego wiersza, a to tylko jedna operacja, brak wywołań funkcji, aby oczekiwać optymalizacji.


1
Twórczy. Wydaje się jednak, że (niepotwierdzone) byłoby to traktowane jako ciąg formatu i może być zoptymalizowane. Z drugiej strony podejście „+” OP jest najlepsze. Zgodnie ze specyfikacją optymalizacja jest gwarantowana: stackoverflow.com/a/288802/9399618
Meow Cat 2012

4

Dodaj wiele wierszy: użyj @

string query = @"SELECT foo, bar
FROM table
WHERE id = 42";

Dodaj wartości ciągu do środka: użyj $

string text ="beer";
string query = $"SELECT foo {text} bar ";

Ciąg wielu linii Dodaj wartości do środka: użyj $ @

string text ="Customer";
string query = $@"SELECT foo, bar
FROM {text}Table
WHERE id = 42";

3

Jeśli nie chcesz spacji / znaków nowej linii, wydaje się, że dodawanie ciągów działa:

var myString = String.Format(
  "hello " + 
  "world" +
  " i am {0}" +
  " and I like {1}.",
  animalType,
  animalPreferenceType
);
// hello world i am a pony and I like other ponies.

Możesz uruchomić powyższe , jeśli chcesz.


2
Jedną z (nieumyślnie wykazanych) wad tego podejścia jest to, że musisz bardzo uważać, aby uwzględnić miejsca, w których chcesz. Poleciłbym bardziej spójne podejście niż ja (np. Zawsze na początku linii).
rattray

string + string jest jednak tym, od czego zaczął OP.
TamaMcGlinn


-10

Możesz użyć tych dwóch metod:

    private static String ReverseString(String str)
    {
        int word_length = 0;
        String result = "";
        for (int i = 0; i < str.Length; i++)
        {
            if (str[i] == ' ')
            {
                result = " " + result;
                word_length = 0;
            }
            else
            {
                result = result.Insert(word_length, str[i].ToString());
                word_length++;
            }
        }
        return result;
    }
//NASSIM LOUCHANI
    public static string SplitLineToMultiline(string input, int rowLength)
    {
        StringBuilder result = new StringBuilder();
        StringBuilder line = new StringBuilder();

        Stack<string> stack = new Stack<string>(ReverseString(input).Split(' '));

        while (stack.Count > 0)
        {
            var word = stack.Pop();
            if (word.Length > rowLength)
            {
                string head = word.Substring(0, rowLength);
                string tail = word.Substring(rowLength);

                word = head;
                stack.Push(tail);
            }

            if (line.Length + word.Length > rowLength)
            {
                result.AppendLine(line.ToString());
                line.Clear();
            }

            line.Append(word + " ");
        }

        result.Append(line);
        return result.ToString();
    }

W SplitLineToMultiline () musisz zdefiniować ciąg, którego chcesz użyć, a długość wiersza jest bardzo prosta. Dziękuję Ci .

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.