Udostępnij za pośrednictwem


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 obiektów Char tylko do odczytu. Właściwość Length ciągu reprezentuje liczbę obiektów Char, 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 obiektu StringInfo.

string a System.String

W języku C#słowo kluczowe string jest aliasem String; dlatego String i string są równoważne. Użyj podanego aliasu string, ponieważ działa nawet bez using System;. Klasa String zapewnia 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 łańcuchó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 operatora nowego, aby utworzyć obiekt ciągu, z wyjątkiem przypadków inicjowania ciągu z tablicą znaków.

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

Niezmienność ciągów

Obiekty ciągu są niezmienne: nie można ich zmienić po ich utworzeniu. Wszystkie metody String i operatory języka C#, które wydają się modyfikować ciąg, faktycznie zwracają wyniki w nowym obiekcie ciągu. W poniższym przykładzie, gdy zawartość s1 i s2 są łączone 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, który został przypisany do s1, jest zwalniany w celu odzyskania pamięci, ponieważ żaden inny zmienny nie przechowuje 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ż modyfikacja ciągu jest w rzeczywistości utworzeniem nowego ciągu, 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 zmodyfikować zawartość ciągu.

Literały ciągów cytowanych

Literały ciągów cytowanych rozpoczynają się i kończą pojedynczym znakiem podwójnego cudzysłowu (") w tym samym wierszu. Literały ciągów cytowanych najlepiej nadają się do ciągów, które mieszczą się w jednym wierszu i nie zawierają żadnych sekwencji znaków ucieczki. Literał ciągu cudzysłowu 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ągów dosłownych

Literały ciągów dosłownych są wygodniejsze w przypadku ciągów wielowierszowych, zawierających znaki ukośnika lub osadzonych podwójnych cudzysłowów. Ciągi dosłowne zachowują znaki nowej linii jako część 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 przedstawiono niektóre typowe zastosowania ciągów dosłownych:

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

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 stringowe

Począwszy od języka C# 11, można użyć nieprzetworzonych literałów ciągów, aby łatwiej tworzyć ciągi wielowierszowe lub używać 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. surowy literał łańcuchowy:

  • Rozpoczyna się i kończy sekwencją co najmniej trzech znaków podwójnego cudzysłowu ("""). Aby obsługiwać literały ciągu zawierające trzy (lub więcej) powtórzone znaki cudzysłowu, można użyć więcej niż trzech kolejnych znaków zarówno na początku, jak i na końcu sekwencji.
  • 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ą, aby zarówno otwierający, jak i zamykający znak cudzysłowu znajdowały się w osobnych wierszach.
  • W literałach ciągu nieprzetworzonego wielowierszowego wszelkie białe znaki z lewej strony cudzysłowów zamykających są usuwane ze wszystkich wierszy nieprzetworzonego literału ciągu.
  • W literałach ciągu nieprzetworzonego wielowierszowego odstęp po cudzysłowie otwierającym w tym samym wierszu jest ignorowany.
  • W literałach ciągu nieprzetworzonego wielowierszowego linie zawierające tylko odstępy po cudzysłowie otwierającym są uwzględnione w literałach ciągu.

W poniższych przykładach przedstawiono 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 przedstawiono zgłoszone błędy kompilatora 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 nieprzetworzonych ciągów wielowierszowych wymagają sekwencji cudzysłowu otwierającego i zamykającego we własnym wierszu. Trzeci przykład jest nieprawidłowy, ponieważ tekst wychodzi poza sekwencję cudzysłowu zamykającego.

Podczas generowania tekstu zawierającego znaki wymagające sekwencje ucieczki podczas używania literałów ciągu cudzysłownego lub literałów ciągu dosłownego należy wziąć pod uwagę nieprzetworzone literały ciągu. Nieprzetworzone literały ciągu są łatwiejsze do odczytania, ponieważ bardziej przypomina 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"
  ]
}
""";

Sekwencje ucieczki ciągów

Sekwencja ucieczki Nazwa postaci Kodowanie Unicode
\' Pojedynczy cudzysłów 0x0027
\" Podwójny cudzysłów 0x0022
\\ Ukośnik odwrotny 0x005C
\0 Zero 0x0000
\a Alarm 0x0007
\b Klawisz Backspace 0x0008
\e Ucieczka 0x001B
\f Źródło danych formularzy 0x000C
\n Nowy wiersz 0x000A
\r Powrót karetki 0x000D
\t Tabulator poziomy 0x0009
\v Tabulator pionowy 0x000B
\u Sekwencja ucieczki Unicode (UTF-16) \uHHHH (zakres: 0000 – FFFF; przykład: \u00E7 = "ç")
\U Sekwencja ucieczki Unicode (UTF-32) \U00HHHHHH (zakres: 0000000–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 lub \xE7 = "ç")

Ostrzeżenie

W przypadku używania sekwencji ucieczki \x i określania mniej niż 4 cyfr szesnastkowych, jeśli znaki, które bezpośrednio następują po sekwencji ucieczki, są prawidłowymi cyframi szesnastkowymi (tj. 0–9, A-F i a-f), będą interpretowane jako część sekwencji ucieczki. Na przykład \xA1 generuje znak "¡", czyli punkt kodu U+00A1. Jeśli jednak następny znak to "A" lub "a", zamiast tego sekwencja ucieczki zostanie zinterpretowana jako \xA1A i zostanie wygenerowana wartość "ਚ", czyli punkt kodu U+0A1A. W takich przypadkach określenie wszystkich 4 cyfr szesnastkowych (na przykład \x00A1)zapobiega ewentualnej błędnej interpretacji.

Notatka

W czasie kompilacji ciągi dosłowne i nieprzetworzone są konwertowane na zwykłe ciągi ze wszystkimi tymi samymi sekwencjami ucieczki. W związku z tym, jeśli w oknie obserwatora debugera wyświetlisz ciąg dosłowny lub nieprzetworzony, zobaczysz znaki ucieczki dodane przez kompilator, a nie wersję dosłowną lub nieprzetworzoną z twojego kodu źródłowego. Na przykład ciąg dosłowny @"C:\files.txt" pojawi się 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. Wszystko wewnątrz nawiasów klamrowych ({...}) jest rozpoznawane jako wartość i wyjściowe jako sformatowany ciąg znaków w trakcie wykonywania. Istnieją dwie metody tworzenia ciągów formatu: interpolacja ciągów i formatowanie złożone.

Interpolacja ciągów

Deklarujesz ciągi interpolowane za pomocą znaku specjalnego $. Ciąg interpolowany zawiera wyrażenia interpolowane w nawiasach klamrowych. Jeśli dopiero zaczynasz interpolację ciągów, zobacz Interpolacja ciągów — interaktywny samouczek języka C#, aby uzyskać krótkie omówienie.

Użyj interpolacji ciągów, aby zwiększyć czytelność i konserwację kodu. Interpolacja ciągów osiąga te same wyniki co metoda String.Format, 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.

Za pomocą interpolacji ciągów można 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. Rozpoczynasz i kończysz łańcuch 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ę { i } znaków początkowych i końcowych interpolacji. W danych wyjściowych znajduje się dowolna sekwencja mniejszej liczby znaków { lub }. W poniższym przykładzie pokazano, jak za pomocą tej funkcji można wyświetlić odległość punktu od punktu początkowego 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.

Interpolacja ciągów w postaci dosłownej

Język C# umożliwia również interpolację ciągów dosłownych, na przykład w wielu wierszach, przy użyciu składni $@ lub @$.

Aby interpretować sekwencje ucieczki dosłownie, użyj literału ciągu . Ciąg interpolowany dosłowny rozpoczyna się od znaku $, po którym następuje znak @. Tokeny $ i @ można używać w dowolnej kolejności: zarówno $@"...", jak i @$"..." są prawidłowymi dosłownymi ciągami z interpolacją.

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}.");

// Output:
// Jupiter Hammon
//     was an African American poet born in 1711.
// He was first published in 1761
// at the age of 50.

Formatowanie złożone

String.Format używa symboli zastępczych w nawiasach klamrowych, aby utworzyć łańcuch formatu. W tym przykładzie wyniki są podobne do metody interpolacji ciągów użytej w poprzednim przykładzie.

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ągi

Podciąg to dowolna sekwencja znaków zawarta w ciągu. Użyj metody Substring, aby utworzyć nowy ciąg na podstawie części oryginalnego ciągu. Możesz wyszukać co najmniej jedno wystąpienie podciągów przy użyciu metody IndexOf. Użyj metody Replace, aby zastąpić wszystkie wystąpienia określonego podciągu nowym ciągiem. Podobnie jak metoda Substring, Replace rzeczywiście zwraca nowy ciąg i nie modyfikuje oryginalnego ciągu. Aby uzyskać więcej informacji, zobacz Jak wyszukiwać ciągi i Jak zmodyfikować 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 pokazano 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"

Jeśli metody String nie zapewniają funkcjonalności, które musisz mieć, aby móc zmodyfikować poszczególne znaki w ciągu, możesz użyć obiektu StringBuilder, aby zmodyfikować poszczególne znaki bezpośrednio, a następnie utworzyć nowy ciąg do przechowywania wyników przy użyciu metod StringBuilder. W poniższym przykładzie przyjęto założenie, ż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 o wartości null i puste ciągi

Pusty ciąg to wystąpienie obiektu System.String 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ć dla pustych ciągów, ponieważ są prawidłowymi obiektami System.String. Puste ciągi są inicjowane w następujący sposób:

string s = String.Empty;

Natomiast ciąg o wartości null nie odnosi się do wystąpienia obiektu System.String, 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 o wartości 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 na ciągach na platformie .NET są wysoce zoptymalizowane i w większości przypadków nie mają znaczącego wpływu na wydajność. Jednak w niektórych scenariuszach, takich jak intensywnie obciążone pętle, które są wykonywane setki lub tysiące razy, operacje na ciągach znakó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, co nie jest obsługiwane przez wbudowany typ danych dla ciągów. 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 obiekt StringBuilder 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

Ponieważ typ String implementuje IEnumerable<T>, można użyć metod rozszerzeń zdefiniowanych w klasie Enumerable w ciągach. Aby uniknąć bałaganu wizualnego, te metody są wykluczone z funkcji IntelliSense dla typu String, 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.