Ciągi i literały ciągu

Ciąg jest obiektem typu String , którego wartość jest tekstem. Wewnętrznie tekst jest przechowywany jako sekwencyjny zbiór Char obiektów tylko do odczytu. Na końcu ciągu języka C# nie ma znaku zakończenia wartości null; dlatego ciąg języka C# może zawierać dowolną liczbę osadzonych znaków null ('\0'). Właściwość Length ciągu reprezentuje liczbę Char obiektów, które zawiera, a nie liczbę znaków Unicode. Aby uzyskać dostęp do poszczególnych punktów kodu Unicode w ciągu, użyj StringInfo obiektu .

string a System.String

W języku C# string słowo kluczowe jest aliasem .String String W związku z tym i string są równoważne, niezależnie od tego, że zaleca się użycie podanego aliasustring, ponieważ działa nawet bez using System;. Klasa String udostępnia wiele metod bezpiecznego tworzenia, manipulowania i porównywania ciągów. Ponadto język C# przeciąża niektóre operatory, aby uprościć typowe operacje na ciągach. Aby uzyskać więcej informacji na temat słowa kluczowego, zobacz ciąg. Aby uzyskać więcej informacji na temat typu i jego metod, zobacz String.

Deklarowanie i inicjowanie ciągów

Ciągi można zadeklarować i zainicjować na różne sposoby, jak pokazano w poniższym przykładzie:

// Declare without initializing.
string message1;

// Initialize to null.
string message2 = null;

// Initialize as an empty string.
// Use the Empty constant instead of the literal "".
string message3 = System.String.Empty;

// Initialize with a regular string literal.
string oldPath = "c:\\Program Files\\Microsoft Visual Studio 8.0";

// Initialize with a verbatim string literal.
string newPath = @"c:\Program Files\Microsoft Visual Studio 9.0";

// Use System.String if you prefer.
System.String greeting = "Hello World!";

// In local variables (i.e. within a method body)
// you can use implicit typing.
var temp = "I'm still a strongly-typed System.String!";

// Use a const string to prevent 'message4' from
// being used to store another string value.
const string message4 = "You can't get rid of me!";

// Use the String constructor only when creating
// a string from a char*, char[], or sbyte*. See
// System.String documentation for details.
char[] letters = { 'A', 'B', 'C' };
string alphabet = new string(letters);

Nie używasz nowego operatora do utworzenia obiektu ciągu, z wyjątkiem inicjowania ciągu za pomocą tablicy znaków.

Zainicjuj ciąg przy użyciu stałej Empty wartości, aby utworzyć nowy String obiekt, którego ciąg ma zero długości. Literał ciągu reprezentujący ciąg o zerowej długości to "". Inicjując Empty ciągi z wartością zamiast wartości null, można zmniejszyć prawdopodobieństwo NullReferenceException wystąpienia. Użyj metody statycznej IsNullOrEmpty(String) , aby zweryfikować wartość ciągu przed próbą uzyskania do niego dostępu.

Niezmienność ciągów

Obiekty ciągów są niezmienne: nie można ich zmienić po ich utworzeniu. String Wszystkie metody i operatory języka C#, które wydają się modyfikować ciąg, w rzeczywistości zwracają wyniki w nowym obiekcie ciągu. W poniższym przykładzie, gdy zawartość elementu s1 i s2 jest połączona w celu utworzenia pojedynczego ciągu, dwa oryginalne ciągi są niezmodyfikowane. Operator += tworzy nowy ciąg zawierający połączoną zawartość. Ten nowy obiekt jest przypisywany do zmiennej s1, a oryginalny obiekt, do którego przypisano s1 , jest zwalniany do odzyskiwania pamięci, ponieważ żadna inna zmienna nie zawiera odwołania do niego.

string s1 = "A string is more ";
string s2 = "than the sum of its chars.";

// Concatenate s1 and s2. This actually creates a new
// string object and stores it in s1, releasing the
// reference to the original object.
s1 += s2;

System.Console.WriteLine(s1);
// Output: A string is more than the sum of its chars.

Ponieważ ciąg "modyfikacja" jest w rzeczywistości nowym tworzeniem ciągów, należy zachować ostrożność podczas tworzenia odwołań do ciągów. Jeśli utworzysz odwołanie do ciągu, a następnie "zmodyfikuj" oryginalny ciąg, odwołanie będzie nadal wskazywać oryginalny obiekt zamiast nowego obiektu, który został utworzony podczas modyfikacji ciągu. Poniższy kod ilustruje to zachowanie:

string str1 = "Hello ";
string str2 = str1;
str1 += "World";

System.Console.WriteLine(str2);
//Output: Hello

Aby uzyskać więcej informacji na temat tworzenia nowych ciągów opartych na modyfikacjach, takich jak operacje wyszukiwania i zastępowania oryginalnego ciągu, zobacz Jak modyfikować zawartość ciągu.

Literały ciągów w cudzysłowach

Literały ciągu cudzysłowu są rozpoczynane i kończą się pojedynczym znakiem podwójnego cudzysłowu (") w tym samym wierszu. Literały ciągów cytowanych najlepiej nadają się do ciągów pasujących do pojedynczego wiersza i nie zawierają żadnych sekwencji ucieczki. Literał cudzysłowu ciągu musi osadzić znaki ucieczki, jak pokazano w poniższym przykładzie:

string columns = "Column 1\tColumn 2\tColumn 3";
//Output: Column 1        Column 2        Column 3

string rows = "Row 1\r\nRow 2\r\nRow 3";
/* Output:
    Row 1
    Row 2
    Row 3
*/

string title = "\"The \u00C6olean Harp\", by Samuel Taylor Coleridge";
//Output: "The Æolean Harp", by Samuel Taylor Coleridge

Literały ciągu dosłownych

Literały ciągu dosłownego są wygodniejsze w przypadku ciągów wielowierszowych, ciągów zawierających znaki ukośnika odwrotnego lub osadzonych podwójnych cudzysłowów. Ciągi dosłowne zachowują nowe znaki wiersza w ramach tekstu ciągu. Użyj podwójnych cudzysłowów, aby osadzić cudzysłów wewnątrz ciągu dosłownego. W poniższym przykładzie pokazano niektóre typowe zastosowania ciągów dosłownych:

string filePath = @"C:\Users\scoleridge\Documents\";
//Output: C:\Users\scoleridge\Documents\

string text = @"My pensive SARA ! thy soft cheek reclined
    Thus on mine arm, most soothing sweet it is
    To sit beside our Cot,...";
/* Output:
My pensive SARA ! thy soft cheek reclined
    Thus on mine arm, most soothing sweet it is
    To sit beside our Cot,...
*/

string quote = @"Her name was ""Sara.""";
//Output: Her name was "Sara."

Nieprzetworzone literały ciągu

Począwszy od języka C# 11, można użyć literałów nieprzetworzonych ciągów , aby łatwiej tworzyć ciągi wielowierszowe lub używać dowolnych znaków wymagających sekwencji ucieczki. Nieprzetworzone literały ciągu usuwają potrzebę używania sekwencji ucieczki. Możesz napisać ciąg, w tym formatowanie białych znaków, sposób wyświetlania go w danych wyjściowych. Nieprzetworzone literał ciągu:

  • Rozpoczyna się i kończy sekwencją co najmniej trzech znaków podwójnego cudzysłowu ("""). Można rozpocząć i zakończyć sekwencję więcej niż trzy kolejne znaki, aby obsługiwać literały ciągu zawierające trzy (lub więcej) powtórzone znaki cudzysłowu.
  • Literały ciągu nieprzetworzonego w jednym wierszu wymagają znaków cudzysłowu otwierającego i zamykającego w tym samym wierszu.
  • Literały nieprzetworzonych ciągów wielowierszowych wymagają zarówno znaków cudzysłowu otwierającego, jak i zamykającego we własnym wierszu.
  • W literałach ciągu nieprzetworzonego wielowierszowego wszystkie białe znaki z lewej strony cudzysłowów zamykających są usuwane.

W poniższych przykładach pokazano następujące reguły:

string singleLine = """Friends say "hello" as they pass by.""";
string multiLine = """
    "Hello World!" is typically the first program someone writes.
    """;
string embeddedXML = """
       <element attr = "content">
           <body style="normal">
               Here is the main text
           </body>
           <footer>
               Excerpts from "An amazing story"
           </footer>
       </element >
       """;
// The line "<element attr = "content">" starts in the first column.
// All whitespace left of that column is removed from the string.

string rawStringLiteralDelimiter = """"
    Raw string literals are delimited 
    by a string of at least three double quotes,
    like this: """
    """";

W poniższych przykładach pokazano błędy kompilatora zgłoszone na podstawie następujących reguł:

// CS8997: Unterminated raw string literal.
var multiLineStart = """This
    is the beginning of a string 
    """;

// CS9000: Raw string literal delimiter must be on its own line.
var multiLineEnd = """
    This is the beginning of a string """;

// CS8999: Line does not start with the same whitespace as the closing line
// of the raw string literal
var noOutdenting = """
    A line of text.
Trying to outdent the second line.
    """;

Pierwsze dwa przykłady są nieprawidłowe, ponieważ literały ciągu nieprzetworzonego wielowierszowego wymagają sekwencji cudzysłowu otwierającego i zamykającego we własnym wierszu. Trzeci przykład jest nieprawidłowy, ponieważ tekst jest wcięta z sekwencji cudzysłowu zamykającego.

Podczas generowania tekstu zawierającego znaki wymagające sekwencji ucieczki należy wziąć pod uwagę nieprzetworzone literały ciągu lub literały ciągu dosłownego. Nieprzetworzone literały ciągu będą łatwiejsze do odczytania przez Ciebie i inne osoby, ponieważ będą bardziej przypominały tekst wyjściowy. Rozważmy na przykład następujący kod, który zawiera ciąg sformatowanego kodu JSON:

string jsonString = """
{
  "Date": "2019-08-01T00:00:00-07:00",
  "TemperatureCelsius": 25,
  "Summary": "Hot",
  "DatesAvailable": [
    "2019-08-01T00:00:00-07:00",
    "2019-08-02T00:00:00-07:00"
  ],
  "TemperatureRanges": {
    "Cold": {
      "High": 20,
      "Low": -10
    },
    "Hot": {
      "High": 60,
      "Low": 20
    }
            },
  "SummaryWords": [
    "Cool",
    "Windy",
    "Humid"
  ]
}
""";

Porównaj ten tekst z tekstem równoważnym w naszym przykładzie na serializacji JSON, która nie korzysta z tej nowej funkcji.

Sekwencje ucieczki ciągów

Sekwencja ucieczki Nazwa znaku Kodowanie Unicode
\' Pojedynczy cudzysłów 0x0027
\" Cudzysłów podwójny 0x0022
\\ Ukośnik odwrotny 0x005C
\0 Zero 0x0000
\a Alerty 0x0007
\b Backspace 0x0008
\f Źródło danych formularza 0x000C
\n Nowy wiersz 0x000A
\r Powrót karetki 0x000D
\t Karta pozioma 0x0009
\v Karta pionowa 0x000B
\u Sekwencja unikowa Unicode (UTF-16) \uHHHH (zakres: 0000 – FFFF; przykład: \u00E7 = "ç")
\U Sekwencja unikowa Unicode (UTF-32) \U00HHHHHH (zakres: 000000–10FFFF; przykład: \U0001F47D = "👽")
\x Sekwencja ucieczki Unicode podobna do "\u" z wyjątkiem zmiennej długości \xH[H][H][H] (zakres: 0 - FFFF; przykład: \x00E7 lub \x0E7\xE7 = "ç")

Ostrzeżenie

W przypadku korzystania z \x sekwencji ucieczki i określania mniejszej niż 4 cyfry szesnastkowej, jeśli znaki, które są natychmiast zgodne z sekwencją ucieczki, są prawidłowymi cyframi szesnastkowymi (tj. 0-9, A-F i f), będą one interpretowane jako część sekwencji ucieczki. Na przykład \xA1 tworzy ciąg "^", czyli punkt kodu U+00A1. Jeśli jednak następny znak to "A" lub "a", sekwencja ucieczki zostanie zinterpretowana jako będąca \xA1A i wygenerowana wartość "ਚ", czyli punkt kodu U+0A1A. W takich przypadkach określenie wszystkich cyfr szesnastkowej (np. \x00A1 ) uniemożliwi wszelkie możliwe błędne interpretacje.

Uwaga

W czasie kompilacji ciągi dosłowne są konwertowane na zwykłe ciągi ze wszystkimi tymi samymi sekwencjami ucieczki. W związku z tym, jeśli wyświetlisz ciąg dosłowny w oknie zegarka debugera, zobaczysz znaki ucieczki, które zostały dodane przez kompilator, a nie wersję dosłowną z kodu źródłowego. Na przykład ciąg @"C:\files.txt" dosłowny będzie wyświetlany w oknie zegarka jako "C:\\files.txt".

Formatowanie ciągów

Ciąg formatu to ciąg, którego zawartość jest określana dynamicznie w czasie wykonywania. Ciągi formatu są tworzone przez osadzanie wyrażeń interpolowanych lub symboli zastępczych wewnątrz nawiasów klamrowych w ciągu. Wszystkie elementy wewnątrz nawiasów klamrowych ({...}) zostaną rozpoznane jako wartość i dane wyjściowe jako sformatowany ciąg w czasie wykonywania. Istnieją dwie metody tworzenia ciągów formatu: interpolacja ciągów i formatowanie złożone.

Interpolacja ciągów

Dostępne w języku C# 6.0 lub nowszym ciągi interpolowane są identyfikowane przez $ znak specjalny i zawierają wyrażenia interpolowane w nawiasach klamrowych. Jeśli dopiero zaczynasz interpolację ciągów, zobacz samouczek interakcyjny interpolacji ciągów — C# w celu szybkiego omówienia.

Użyj interpolacji ciągów, aby zwiększyć czytelność i możliwość utrzymania kodu. Interpolacja ciągów osiąga te same wyniki co String.Format metoda, ale poprawia łatwość użycia i czytelność śródliniową.

var jh = (firstName: "Jupiter", lastName: "Hammon", born: 1711, published: 1761);
Console.WriteLine($"{jh.firstName} {jh.lastName} was an African American poet born in {jh.born}.");
Console.WriteLine($"He was first published in {jh.published} at the age of {jh.published - jh.born}.");
Console.WriteLine($"He'd be over {Math.Round((2018d - jh.born) / 100d) * 100d} years old today.");

// Output:
// Jupiter Hammon was an African American poet born in 1711.
// He was first published in 1761 at the age of 50.
// He'd be over 300 years old today.

Począwszy od języka C# 10, można użyć interpolacji ciągów, aby zainicjować ciąg stały, gdy wszystkie wyrażenia używane dla symboli zastępczych są również ciągami stałymi.

Począwszy od języka C# 11, można połączyć nieprzetworzone literały ciągu z interpolacjami ciągów. Zaczynasz i kończysz ciąg formatu z co najmniej trzema kolejnymi podwójnymi cudzysłowami. Jeśli ciąg wyjściowy powinien zawierać { znak lub } , możesz użyć dodatkowych $ znaków, aby określić liczbę { znaków rozpoczynających się i } kończących interpolację. Każda sekwencja mniejszej { liczby znaków jest } uwzględniona w danych wyjściowych. W poniższym przykładzie pokazano, jak za pomocą tej funkcji można wyświetlić odległość punktu od źródła i umieścić punkt wewnątrz nawiasów klamrowych:

int X = 2;
int Y = 3;

var pointMessage = $$"""The point {{{X}}, {{Y}}} is {{Math.Sqrt(X * X + Y * Y)}} from the origin.""";

Console.WriteLine(pointMessage);
// Output:
// The point {2, 3} is 3.605551275463989 from the origin.

Złożone formatowanie

Symbole String.Format zastępcze w nawiasach klamrowych umożliwiają utworzenie ciągu formatu. W tym przykładzie są wyświetlane podobne dane wyjściowe do metody interpolacji ciągów użytej powyżej.

var pw = (firstName: "Phillis", lastName: "Wheatley", born: 1753, published: 1773);
Console.WriteLine("{0} {1} was an African American poet born in {2}.", pw.firstName, pw.lastName, pw.born);
Console.WriteLine("She was first published in {0} at the age of {1}.", pw.published, pw.published - pw.born);
Console.WriteLine("She'd be over {0} years old today.", Math.Round((2018d - pw.born) / 100d) * 100d);

// Output:
// Phillis Wheatley was an African American poet born in 1753.
// She was first published in 1773 at the age of 20.
// She'd be over 300 years old today.

Aby uzyskać więcej informacji na temat formatowania typów platformy .NET, zobacz Typy formatowania na platformie .NET.

Podciągów

Podciąg to dowolna sekwencja znaków zawartych w ciągu. Substring Użyj metody , aby utworzyć nowy ciąg z części oryginalnego ciągu. Za pomocą metody można wyszukać co najmniej jedno wystąpienie podciągu IndexOf . Replace Użyj metody , aby zastąpić wszystkie wystąpienia określonego podciągu nowym ciągiem. Podobnie jak metoda Substring , Replace faktycznie zwraca nowy ciąg i nie modyfikuje oryginalnego ciągu. Aby uzyskać więcej informacji, zobacz How to search strings (Jak wyszukiwać ciągi ) i How to modify string contents (Jak modyfikować zawartość ciągu).

string s3 = "Visual C# Express";
System.Console.WriteLine(s3.Substring(7, 2));
// Output: "C#"

System.Console.WriteLine(s3.Replace("C#", "Basic"));
// Output: "Visual Basic Express"

// Index values are zero-based
int index = s3.IndexOf("C");
// index = 7

Uzyskiwanie dostępu do poszczególnych znaków

Możesz użyć notacji tablicy z wartością indeksu, aby uzyskać dostęp tylko do odczytu do poszczególnych znaków, jak w poniższym przykładzie:

string s5 = "Printing backwards";

for (int i = 0; i < s5.Length; i++)
{
    System.Console.Write(s5[s5.Length - i - 1]);
}
// Output: "sdrawkcab gnitnirP"

String Jeśli metody nie udostępniają funkcji, które należy zmodyfikować poszczególne znaki w ciągu, można użyć StringBuilder obiektu do zmodyfikowania poszczególnych znaków "w miejscu", a następnie utworzyć nowy ciąg do przechowywania wyników przy użyciu StringBuilder metod . W poniższym przykładzie załóżmy, że należy zmodyfikować oryginalny ciąg w określony sposób, a następnie zapisać wyniki do użycia w przyszłości:

string question = "hOW DOES mICROSOFT wORD DEAL WITH THE cAPS lOCK KEY?";
System.Text.StringBuilder sb = new System.Text.StringBuilder(question);

for (int j = 0; j < sb.Length; j++)
{
    if (System.Char.IsLower(sb[j]) == true)
        sb[j] = System.Char.ToUpper(sb[j]);
    else if (System.Char.IsUpper(sb[j]) == true)
        sb[j] = System.Char.ToLower(sb[j]);
}
// Store the new string.
string corrected = sb.ToString();
System.Console.WriteLine(corrected);
// Output: How does Microsoft Word deal with the Caps Lock key?

Ciągi zerowe i puste ciągi

Pusty ciąg to wystąpienie System.String obiektu zawierającego zero znaków. Puste ciągi są często używane w różnych scenariuszach programowania do reprezentowania pustego pola tekstowego. Metody można wywołać w pustych ciągach, ponieważ są one prawidłowymi System.String obiektami. Puste ciągi są inicjowane w następujący sposób:

string s = String.Empty;

Natomiast ciąg null nie odwołuje się do wystąpienia System.String obiektu, a każda próba wywołania metody w ciągu null powoduje .NullReferenceException Można jednak używać ciągów null w operacjach łączenia i porównywania z innymi ciągami. W poniższych przykładach pokazano niektóre przypadki, w których odwołanie do ciągu null nie powoduje zgłoszenia wyjątku:

string str = "hello";
string nullStr = null;
string emptyStr = String.Empty;

string tempStr = str + nullStr;
// Output of the following line: hello
Console.WriteLine(tempStr);

bool b = (emptyStr == nullStr);
// Output of the following line: False
Console.WriteLine(b);

// The following line creates a new empty string.
string newStr = emptyStr + nullStr;

// Null strings and empty strings behave differently. The following
// two lines display 0.
Console.WriteLine(emptyStr.Length);
Console.WriteLine(newStr.Length);
// The following line raises a NullReferenceException.
//Console.WriteLine(nullStr.Length);

// The null character can be displayed and counted, like other chars.
string s1 = "\x0" + "abc";
string s2 = "abc" + "\x0";
// Output of the following line: * abc*
Console.WriteLine("*" + s1 + "*");
// Output of the following line: *abc *
Console.WriteLine("*" + s2 + "*");
// Output of the following line: 4
Console.WriteLine(s2.Length);

Używanie narzędzia stringBuilder do szybkiego tworzenia ciągów

Operacje ciągów na platformie .NET są wysoce zoptymalizowane i w większości przypadków nie wpływają znacząco na wydajność. Jednak w niektórych scenariuszach, takich jak ciasne pętle, które są wykonywane wiele setek lub tysięcy razy, operacje ciągów mogą mieć wpływ na wydajność. Klasa StringBuilder tworzy bufor ciągu, który zapewnia lepszą wydajność, jeśli program wykonuje wiele manipulacji ciągami. Ciąg StringBuilder umożliwia również ponowne przypisywanie pojedynczych znaków. Typ danych wbudowanego ciągu nie obsługuje. Ten kod, na przykład, zmienia zawartość ciągu bez tworzenia nowego ciągu:

System.Text.StringBuilder sb = new System.Text.StringBuilder("Rat: the ideal pet");
sb[0] = 'C';
System.Console.WriteLine(sb.ToString());
//Outputs Cat: the ideal pet

W tym przykładzie StringBuilder obiekt służy do tworzenia ciągu na podstawie zestawu typów liczbowych:

var sb = new StringBuilder();

// Create a string composed of numbers 0 - 9
for (int i = 0; i < 10; i++)
{
    sb.Append(i.ToString());
}
Console.WriteLine(sb);  // displays 0123456789

// Copy one character of the string (not possible with a System.String)
sb[0] = sb[9];

Console.WriteLine(sb);  // displays 9123456789

Ciągi, metody rozszerzeń i LINQ

String Ponieważ typ implementuje metodę IEnumerable<T>, można użyć metod rozszerzeń zdefiniowanych w klasie w Enumerable ciągach. Aby uniknąć zaśmiecania wizualnego String , te metody są wykluczone z funkcji IntelliSense dla typu, ale są one jednak dostępne. Możesz również użyć wyrażeń zapytań LINQ w ciągach. Aby uzyskać więcej informacji, zobacz LINQ and Strings (LinQ i ciągi).