Строки и строковые литералы

Строка — это объект типа String, значением которого является текст. Внутри программы текст хранится в виде упорядоченной коллекции объектов Char только для чтения. В конце строки C# нет символа, завершающего значение NULL; поэтому строка C# может содержать любое количество внедренных символов NULL ('\0'). Свойство Length строки соответствует числу содержащихся в ней объектов Char, но не числу символов Юникода. Для доступа к отдельным кодовым точкам Юникода в строке используйте объект StringInfo.

Сравнение строки и System.String

В C# ключевое слово string является псевдонимом для String. Таким образом, String и string эквивалентны, независимо от того, рекомендуется использовать предоставленный псевдоним string , так как он работает даже без using System;. Класс String предоставляет множество методов для безопасного создания, обработки и сравнения строк. Кроме того, язык C# перегружает некоторые операторы для упрощения типичных операций со строками. Дополнительные сведения о ключевых словах см. в статье, посвященной строкам. Дополнительные сведения о типе и его методах см. здесь: String.

Объявление и инициализация строк

Вы можете объявлять и инициализировать строки различными способами, как показано в следующем примере:

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

Новый оператор не используется для создания строкового объекта, за исключением инициализации строки с массивом символов.

Инициализируйте строку с константным значением Empty для создания нового объекта String, строка которого имеет нулевую длину. Представлением строкового литерала строки с нулевой длиной является "". Если вы инициализируете строки со значением Empty вместо NULL, вы снизите вероятность появления исключения NullReferenceException. Используйте статический метод IsNullOrEmpty(String), чтобы проверить значение строки, прежде чем пытаться получить к ней доступ.

Неизменность строк

Строковые объекты неизменяемы: их нельзя изменить после их создания. Может показаться, что все методы String и операторы C# изменяют строку, но в действительности они возвращают результаты в новый строковый объект. Когда содержимое s1 и s2 объединяется для формирования одной строки, две исходные строки не изменяются, как показано в следующем примере. Оператор += создает новую строку, которая содержит объединенное содержимое. Этот новый объект присваивается переменной s1, а исходный объект, который был присвоен s1, освобождается для сборки мусора, так как ни одна переменная не ссылается на него.

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.

Так как "изменение" строки на самом деле является созданием новой строки, создавать ссылки на строки следует с осторожностью. Если вы создадите ссылку на строку, а затем "измените" исходную строку, ссылка будет по-прежнему указывать на исходный объект, а не на новый объект, который был создан при изменении строки. Это поведение проиллюстрировано в следующем коде:

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

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

Сведения о создании новых строк, основанных на таких изменениях, как операции поиска и замены исходной строки, см. в инструкциях по изменению содержимого строки.

Строковые литералы с кавычками

Строковые литералы в кавычках начинаются и заканчиваются одним символом двойной кавычки (") в одной строке. Строковые литералы с кавычками лучше всего подходят для строк, которые помещаются в одну строку и не включают escape-последовательности. Строковый литерал в кавычках должен внедрять escape-символы, как показано в следующем примере:

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

Строковые литералы verbatim

Буквальные строковые литералы более удобны для многостроковых строк, строк, содержащих символы обратной косой черты или внедренные двойные кавычки. Строки verbatim сохраняют новые символы строк в составе текста строки. Используйте двойные кавычки, чтобы вставить кавычки в буквальной строке. В следующем примере показаны наиболее часто используемым буквальные строки:

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."

Необработанные строковые литералы

Начиная с C# 11, можно использовать необработанные строковые литералы для упрощения создания строк, которые являются многостроочными, или использовать любые символы, требующие escape-последовательностей. Необработанные строковые литералы удаляют необходимость использования escape-последовательностей. Вы можете написать строку, включая форматирование пробелов, как она будет отображаться в выходных данных. Необработанный строковый литерал:

  • Начинается и заканчивается последовательность не менее трех символов двойной кавычки ("""). Для поддержки строковых литералов, содержащих три повторяющихся символа кавычки, разрешено начинать и заканчивать последовательность более трех последовательных символов.
  • Однострочные необработанные строковые литералы требуют символов открывающей и закрывающей кавычки в одной строке.
  • Многострочный необработанные строковые литералы требуют как открывающих, так и закрывающих символов кавычки в собственной строке.
  • В многострочных строковых литералах все пробелы слева от закрывающих кавычек удаляются.

В следующих примерах демонстрируются следующие правила:

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: """
    """";

В следующих примерах показаны ошибки компилятора, зарегистрированные на основе следующих правил:

// 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.
    """;

Первые два примера недопустимы, так как многострочный необработанный строковый литерал требует открытия и закрывающей последовательности кавычек в собственной строке. Третий пример недопустим, так как текст вытесняется из закрывающей последовательности кавычек.

При создании текста, включающего символы, требующие escape-последовательностей при использовании строковых литералов с кавычками или строковых литералов, следует учитывать необработанные строковые литералы. Необработанные строковые литералы будут проще читать, так как они будут более похожи на выходной текст. Например, рассмотрим следующий код, включающий строку форматированного 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"
  ]
}
""";

Сравните этот текст с эквивалентным текстом в нашем примере по сериализации JSON, который не использует эту новую функцию.

Escape-последовательности строк

Escape-последовательность Имя символа Кодировка Юникод
\' Одинарная кавычка 0x0027
\" Двойная кавычка 0x0022
\\ Обратная косая черта 0x005C
\0 Null 0x0000
\a Предупреждение 0x0007
\b Backspace 0x0008
\f Перевод страницы 0x000C
\n Новая строка 0x000A
\r Возврат каретки 0x000D
\t Горизонтальная табуляция 0x0009
\v Вертикальная табуляция 0x000B
\u Escape-последовательность Юникода (UTF-16) \uHHHH (диапазон: 0000–FFFF; пример: \u00E7 = "ç")
\U Escape-последовательность Юникода (UTF-32) \U00HHHHHH (диапазон: 000000 – 10FFFF; пример: \U0001F47D = "👽")
\x Escape-последовательность Юникода аналогична "\u", она отличается только длиной переменной \xH[H][H][H] (диапазон: 0–FFFF; пример: \x00E7 или \x0E7 или \xE7 = "ç")

Предупреждение

Если вы используете escape-последовательность \x с менее чем четырьмя шестнадцатеричными цифрами, то когда непосредственно следующие за ней символы также являются допустимыми шестнадцатеричными цифрами (т. е. 0–9, A–F и a–f), они будут интерпретированы как часть этой escape-последовательности. Например, \xA1 создает ""," — кодовую точку U+00A1. Однако если следующий символ — "A" или "a", escape-последовательность будет интерпретирована как являющаяся \xA1A и создающая "ਚ", которая является кодовой точкой U+0A1A. В таких случаях, чтобы избежать некорректной интерпретации, указывайте все четыре шестнадцатеричных знака (например, \x00A1).

Примечание

Во время компиляции буквальные строки преобразуются в обычные строки с теми же escape-последовательностями. Поэтому, если вы просматриваете буквальную строку в окне контрольных значений отладчика, вы увидите escape-символы, добавленные компилятором, а не буквальную версию из исходного кода. Например, строка verbatim @"C:\files.txt" будет отображаться в окне контрольных значений как "C:\\files.txt".

Строки формата

Строка формата — это строка, содержимое которой можно определить динамически во время выполнения. Строки формата создаются путем внедрения интерполированных выражений или заполнителей внутри фигурных скобок в строке. Весь код внутри фигурных скобок ({...}) будет преобразован в значение и выходные данные как отформатированная строка во время выполнения. Существует два способа создания строк формата: интерполяция строк и составное форматирование.

Интерполяция строк

В C# 6.0 и более поздних версий интерполированные строки определяются по специальному символу $. Они включают интерполированные выражения в фигурных скобках. Если вы не знакомы с интерполяцией строк, ознакомьтесь с интерактивным руководством по C# , чтобы ознакомиться с кратким обзором.

Используйте интерполяцию для повышения удобства чтения и обслуживаемости кода. Интерполяция строк позволяет достичь тех же результатов, что и использование метода String.Format, но более простым и понятным способом.

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.

Начиная с C# 10, можно использовать интерполяцию строк для инициализации константной строки, если все выражения, используемые для заполнителей, также являются константными строками.

Начиная с C# 11, можно объединить необработанные строковые литералы с интерполяцией строк. Вы начинаете и заканчиваете строку формата тремя или более последовательными двойными кавычками. Если выходная строка должна содержать { символ или } символ, можно использовать дополнительные $ символы, чтобы указать, сколько }{ символов начинается и заканчивается интерполяцией. В выходные данные включается любая последовательность меньшего количества { символов или } символов. В следующем примере показано, как использовать эту функцию для отображения расстояния точки от источника и размещения точки внутри фигурных скобок:

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.

Составное форматирование

String.Format использует заполнители в фигурных скобках, чтобы создать строку формата. В этом примере результат аналогичен выходным данным, получаемым с помощью метода интерполяции строк, описанного выше.

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.

Дополнительные сведения о форматировании типов .NET см. в разделе "Типы форматирования" в .NET.

Подстроки

Подстрока — это последовательность символов, содержащихся в строке. Используйте метод Substring, чтобы создать новую строку из части исходной строки. Одно вхождение подстроки или несколько можно найти с помощью метода IndexOf. Используйте метод Replace, чтобы заменить все вхождения указанной подстроки новой строкой. Как и метод Substring , Replace фактически возвращает новую строку и не изменяет исходную строку. См. дополнительные сведения о поиске строк и изменении содержимого строк.

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

Доступ к отдельным символам

Используя нотацию массива со значением индекса, можно получить доступ только для чтения к отдельным символам, как показано в следующем примере:

string s5 = "Printing backwards";

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

String Если методы не предоставляют функциональные возможности, которые необходимо изменить отдельные символы в строке, можно использовать StringBuilder объект для изменения отдельных символов на месте, а затем создать новую строку для хранения результатов с помощью StringBuilder методов. В следующем примере предположим, что необходимо определенным образом изменить исходную строку, а затем сохранить результаты для дальнейшего использования:

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?

Пустые строки и пустые строки

Пустая строка — это экземпляр объекта System.String, который содержит нуль символов. Пустые строки часто используются в различных сценариях программирования для представления пустого текстового поля. Методы можно вызывать для пустых строк, так как они допустимы System.String . Пустые строки инициализируются следующим образом:

string s = String.Empty;

Напротив, строка NULL не ссылается на экземпляр System.String объекта, и любая попытка вызова метода в строке NULL вызывает исключение NullReferenceException. Но вы можете использовать строки NULL в операциях объединения и сравнения с другими строками. В следующих примерах показаны некоторые случаи, в которых ссылка на пустую строку не вызывает исключение:

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

Использование stringBuilder для быстрого создания строк

Строковые операции в .NET оптимизированы и в большинстве случаев не влияют на производительность. Но в некоторых сценариях, например в сплошных циклах, которые выполняются сотни и тысячи раз, операции со строками могут повлиять на производительность. Класс StringBuilder создает строковый буфер, который ускоряет работу, если программа выполняет много операций над строками. Строка StringBuilder также позволяет переназначить отдельные символы, что не поддерживает встроенный строковый тип данных. Например, этот код изменяет содержимое строки без создания новой строки:

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

В этом примере объект StringBuilder используется для создания строки из набора числовых типов:

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

Строки, методы расширения и LINQ

Так как тип String использует IEnumerable<T>, вы можете применять методы расширения, определенные для строк в классе Enumerable. Чтобы избежать визуального загромождений, эти методы исключаются из IntelliSense для String типа, но они доступны тем не менее. Можно также использовать выражения запроса LINQ в строках. Дополнительные сведения см. в документации по LINQ и строкам.