Menu

String w C#: Length, porównywanie, znaki ucieczki, stringi dosłowne i wieloliniowe

Jak działają stringi w C#: niemutowalność, Length i indeksowanie, porównywanie przez ==, Equals i string.Compare, znaki ucieczki, stringi dosłowne z @ i tekst wieloliniowy, surowe literały stringów, sprawdzanie null i pustych wartości oraz przechodzenie po znakach.

Na tej stronie są działające edytory: edytuj, uruchamiaj i od razu zobacz wynik.

string w C# to ciąg znaków UTF-16 (wartości char). To typ referencyjny, ale w istotnych kwestiach zachowuje się jak wartość: jest niemutowalny, a == porównuje tekst, a nie tożsamość obiektu. string to słowo kluczowe C# dla typu .NET System.String; obu można używać zamiennie.

Wynik:

Porto
5
0
*****
hi
Porto has 5 letters

Stringi są niemutowalne

Żadna metoda nie zmienia stringa w miejscu. ToUpper, Replace, Trim i każda inna metoda zwracają nowy string, a oryginał zostaje nietknięty. Zapominanie o użyciu zwracanej wartości to bardzo częsty błąd przy stringach:

Wynik:

[  maya  ]
[MAYA]
tea
tea and cake

Dzięki niemutowalności stringi można bezpiecznie współdzielić między wątkami i używać jako kluczy słownika. Kosztem jest to, że budowanie stringa kawałek po kawałku w pętli tworzy nowy string na każdym kroku; do tego służy StringBuilder.

Length i indeksowanie

Length to właściwość (bez nawiasów). Indeksator s[i] zwraca char na pozycji i, licząc od 0, i służy tylko do odczytu:

Wynik:

7
A
1
IndexOutOfRangeException
XB-2931

C# 8 i nowsze przyjmują też indeks liczony od końca: code[^1] to ostatni znak, to samo co code[code.Length - 1]. Żeby pobrać kilka znaków naraz, zobacz substring.

Length liczy jednostki kodowe UTF-16, a nie to, co czytelnik widzi jako znaki. Większość liter w większości języków to jedna jednostka, ale emoji i niektóre rzadkie pisma używają dwóch (para surogatów), więc "👍".Length wynosi 2.

Porównywanie stringów

== i != porównują znaki z rozróżnianiem wielkości liter. W każdym innym przypadku przekaż wartość StringComparison, która mówi, jak porównywać:

Wynik:

False
False
True
True
apple first
True
False
False
True

Którego porównania użyć:

  • Ordinal porównuje liczbowe kody znaków. Jest szybkie, przewidywalne i takie samo na każdej maszynie. Używaj go dla identyfikatorów, nazw plików, kluczy, wartości protokołów i wszystkiego, czego nie pokazujesz człowiekowi.
  • OrdinalIgnoreCase działa tak samo, ale ignoruje wielkość liter. To właściwy wybór do sprawdzeń bez rozróżniania wielkości liter, takich jak rozszerzenia plików czy nagłówki HTTP.
  • CurrentCulture (i CurrentCultureIgnoreCase) stosuje reguły języka użytkownika, więc sortuje słowa tak, jak słownik w tym języku. Używaj go do sortowania list pokazywanych użytkownikom.
  • InvariantCulture stosuje reguły zależne od kultury, które nie zależą od ustawień językowych użytkownika. Nadal zależy jednak od biblioteki globalizacji: współczesny .NET używa ICU, .NET Framework używa NLS z Windows, a te dwie mogą inaczej porządkować niektóre stringi.

string.Compare(a, b) i a.CompareTo(b) bez StringComparison używają bieżącej kultury, przez co ich wyniki różnią się między maszynami. Unikaj porównywania przez a.ToLower() == b.ToLower(): tworzy to dwa nowe stringi i w niektórych kulturach daje błędne odpowiedzi (klasycznym przykładem jest tureckie i z kropką i bez kropki).

Wywołanie a.Equals(b), gdy a jest null, rzuca NullReferenceException, a a == b i statyczne string.Equals(a, b) bezpiecznie obsługują null.

Znaki ucieczki

W zwykłym literale stringa ukośnik wsteczny rozpoczyna sekwencję ucieczki:

SekwencjaZnaczenie
\nNowa linia (line feed)
\rPowrót karetki
\tTabulator
\\Ukośnik wsteczny
\"Cudzysłów
\'Apostrof (potrzebny tylko w literale char)
\0Znak null
\uXXXXZnak UTF-16 według 4-cyfrowego kodu szesnastkowego, np. \u00e9
\U0001F44DZnak według 8-cyfrowego kodu szesnastkowego; powyżej U+FFFF staje się dwiema wartościami char
\xH do \xHHHHZnak według od 1 do 4 cyfr szesnastkowych (unikaj: długość jest niejednoznaczna)
\a, \b, \f, \vSygnał dźwiękowy, backspace, wysuw strony, tabulator pionowy
\eZnak escape, U+001B (C# 13)

Wynik:

Name:	Ana
City:	Porto
She said "hi"
C:\temp\report.txt
cafe = cafe
' is a quote char

Nierozpoznana sekwencja, taka jak "\d", to błąd kompilacji (CS1009, unrecognized escape sequence), i właśnie to się dzieje, gdy ścieżkę Windows albo wyrażenie regularne zapiszesz jako zwykły string.

Stringi dosłowne i wieloliniowe

Poprzedzenie literału znakiem @ czyni go dosłownym (verbatim): ukośniki wsteczne są zwykłymi znakami, a string może zajmować kilka linii. Jedyną pozostałą sekwencją ucieczki jest "", która daje jeden cudzysłów.

Wynik:

C:\Users\ana\Documents\notes.txt
\d{3}-\d{4}
He said "ready" and left.
Rua das Flores 12
4050-262 Porto
Portugal
first line
second line

Dosłowny string wieloliniowy zawiera wszystko między cudzysłowami, łącznie z wcięciem kolejnych linii, dlatego zwykle zapisuje się je przy samym marginesie, jak powyżej. Znaki końca linii w środku są takie jak w pliku źródłowym, więc plik zapisany z końcami linii Windows daje \r\n.

C# 11 dodał surowe literały stringów (raw string literals), które rozwiązują oba problemy. Zaczynają się i kończą trzema lub więcej cudzysłowami, w ogóle nie wymagają znaków ucieczki i usuwają wcięcie równe wcięciu cudzysłowów zamykających:

// C# 11 and later
string json = """
    {
      "name": "Ana",
      "path": "C:\temp"
    }
    """;
// The 4 spaces before the closing """ are removed from every line.

Jeśli sama treść zawiera """, zacznij i zakończ czterema cudzysłowami. Surowe literały łączą się też z interpolacją ($"""..."""); zobacz interpolacja stringów.

Null, pusty string i białe znaki

Zmienna typu string może mieć wartość null (brak stringa), być pusta ("", długość 0) albo zawierać same białe znaki. Dwie statyczne metody pomocnicze sprawdzają te przypadki bez ryzyka NullReferenceException:

Wynik:

null    empty: True   blank: True
""      empty: True   blank: True
"   "   empty: False  blank: True
"Ana"   empty: False  blank: False
True

Przy walidacji danych od użytkownika prawie zawsze potrzebujesz string.IsNullOrWhiteSpace. string.Empty i "" to ta sama wartość; to, którą zapiszesz, jest kwestią stylu.

Przechodzenie po znakach

String jest enumerowalny, więc foreach odwiedza każdy char. Typ char ma statyczne metody do klasyfikowania znaków:

Wynik:

letters=6 digits=4 upper=1 symbols=1
!6202tesnuS

Łączenie stringów

+ łączy stringi i działa z dowolnym typem po drugiej stronie (wywołuje ToString()). Do łączenia wielu wartości lepsza jest interpolacja albo string.Join:

Wynik:

Ana Silva, 31
Ana Silva, 31
AnaSilva
tea | coffee | juice
Total: 23
Total: 5

Dwie ostatnie linie pokazują pułapkę kolejności działań: + jest obliczany od lewej do prawej, więc gdy lewa strona jest już stringiem, każdy kolejny + oznacza konkatenację. Arytmetykę wewnątrz konkatenacji otaczaj nawiasami.

Najczęściej zadawane pytania

Jak sprawdzić długość stringa w C#?

Użyj właściwości Length: "hello".Length to 5. To właściwość, a nie metoda, więc nie ma nawiasów. Length liczy jednostki kodowe UTF-16, więc emoji lub inny znak spoza podstawowej płaszczyzny wielojęzycznej (BMP) liczy się jako 2. Wywołanie na stringu null rzuca NullReferenceException.

Jak porównywać stringi w C#?

a == b porównuje tekst i rozróżnia wielkość liter. Do porównania bez rozróżniania wielkości liter użyj string.Equals(a, b, StringComparison.OrdinalIgnoreCase). Do sortowania i ustalania kolejności służy string.Compare(a, b, StringComparison.Ordinal) albo porównanie zależne od kultury, które zwraca liczbę ujemną, zero lub liczbę dodatnią.

Jak zapisać string wieloliniowy w C#?

Poprzedź literał znakiem @, żeby był dosłowny: może zajmować kilka linii, ukośniki wsteczne są zwykłymi znakami, a cudzysłów zapisuje się jako "". W C# 11 i nowszych surowe literały stringów ograniczone przez """ też mogą zajmować kilka linii, usuwają wspólne wcięcie i w ogóle nie wymagają znaków ucieczki. Linie możesz też łączyć przez \n albo Environment.NewLine.

Czym jest string dosłowny (verbatim) w C#?

To literał stringa poprzedzony znakiem @, na przykład @"C:\Users\ana". Sekwencje ucieczki nie są przetwarzane, więc ukośnik wsteczny to po prostu ukośnik, co czyni go naturalnym wyborem dla ścieżek Windows i wyrażeń regularnych. Jedyną sekwencją ucieczki w środku jest "" dla cudzysłowu.

Jaka jest różnica między string a String w C#?

Żadna. string to słowo kluczowe C# będące aliasem typu .NET System.String, tak jak int jest aliasem System.Int32. Przyjęło się używać string w deklaracjach i string.IsNullOrEmpty(...) w wywołaniach statycznych, ale obie formy kompilują się do dokładnie tego samego.

Jakie znaki ucieczki obsługuje C#?

\n nowa linia, \r powrót karetki, \t tabulator, \\ ukośnik wsteczny, \" cudzysłów, \' apostrof, \0 znak null, \uXXXX znak UTF-16 według kodu szesnastkowego, \U0001F600 znak według 8-cyfrowego kodu (staje się parą surogatów), \a sygnał dźwiękowy, \b backspace, \f wysuw strony, \v tabulator pionowy oraz \e (C# 13) znak escape. Nieznana sekwencja, taka jak \d, to błąd kompilacji (CS1009), dlatego wyrażenia regularne zwykle zapisuje się jako stringi dosłowne.

Ilustracja języków programowania w Coddy

Ucz się programowania z Coddy

ZACZNIJ