Menu

Строки в C#: длина, сравнение, escape-последовательности, verbatim и многострочные строки

Как работают строки в C#: неизменяемость, Length и индексация, сравнение через ==, Equals и string.Compare, escape-последовательности, verbatim-строки с @ и многострочный текст, необработанные строковые литералы, проверки на null и пустоту, перебор символов.

На этой странице есть исполняемые редакторы: меняйте, запускайте и сразу видите результат.

string в C# это последовательность символов UTF-16 (значений char). Это ссылочный тип, но в важных отношениях он ведёт себя как значение: он неизменяемый, а == сравнивает текст, а не идентичность объектов. string это ключевое слово C# для типа .NET System.String; они взаимозаменяемы.

Вывод:

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

Строки неизменяемы

Ни один метод не изменяет строку на месте. ToUpper, Replace, Trim и все остальные методы возвращают новую строку и не трогают исходную. Забыть использовать возвращаемое значение очень частая ошибка при работе со строками:

Вывод:

[  maya  ]
[MAYA]
tea
tea and cake

Неизменяемость делает строки безопасными для совместного использования между потоками и для использования в качестве ключей словаря. Цена в том, что сборка строки по частям в цикле создаёт новую строку на каждом шаге; для этого используйте StringBuilder.

Длина и индексация

Length это свойство (без скобок). Индексатор s[i] возвращает char в позиции i, считая с 0, и доступен только для чтения:

Вывод:

7
A
1
IndexOutOfRangeException
XB-2931

C# 8 и новее принимают также индекс с конца: code[^1] это последний символ, то же самое, что code[code.Length - 1]. Чтобы взять несколько символов сразу, см. подстроки.

Length считает кодовые единицы UTF-16, а не то, что читатель видит как символы. Большинство букв в большинстве языков занимают одну единицу, но эмодзи и некоторые редкие письменности используют две (суррогатную пару), поэтому "👍".Length равно 2.

Сравнение строк

== и != сравнивают символы с учётом регистра. Во всех остальных случаях передавайте значение StringComparison, которое говорит, как сравнивать:

Вывод:

False
False
True
True
apple first
True
False
False
True

Какое сравнение использовать:

  • Ordinal сравнивает числовые коды символов. Он быстрый, предсказуемый и одинаковый на любой машине. Используйте его для идентификаторов, имён файлов, ключей, значений протоколов и всего, что не показывается человеку.
  • OrdinalIgnoreCase то же самое, но без учёта регистра. Правильный выбор для проверок без учёта регистра, например расширений файлов или HTTP-заголовков.
  • CurrentCulture (и CurrentCultureIgnoreCase) следует правилам языка пользователя, поэтому сортирует слова так, как это сделал бы словарь этого языка. Используйте его для сортировки списков, которые видят пользователи.
  • InvariantCulture использует правила с учётом культуры, которые не зависят от языковых настроек пользователя. Он всё равно зависит от библиотеки глобализации: современный .NET использует ICU, .NET Framework использует NLS Windows, и они могут по-разному упорядочивать некоторые строки.

string.Compare(a, b) и a.CompareTo(b) без StringComparison используют текущую культуру, поэтому их результаты различаются на разных машинах. Не сравнивайте через a.ToLower() == b.ToLower(): это создаёт две новые строки и в некоторых культурах даёт неверные ответы (классический случай турецкие i с точкой и без точки).

Вызов a.Equals(b), когда a равно null, выбрасывает NullReferenceException, тогда как a == b и статический string.Equals(a, b) безопасно обрабатывают null.

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

Внутри обычного строкового литерала обратный слеш начинает escape-последовательность:

ПоследовательностьЗначение
\nПеревод строки (line feed)
\rВозврат каретки
\tТабуляция
\\Обратный слеш
\"Двойная кавычка
\'Одинарная кавычка (нужна только в литерале char)
\0Нулевой символ
\uXXXXСимвол UTF-16 по 4-значному шестнадцатеричному коду, например \u00e9
\U0001F44DСимвол по 8-значному шестнадцатеричному коду; выше U+FFFF превращается в два значения char
\xH to \xHHHHСимвол по 1 до 4 шестнадцатеричных цифр (избегайте: длина неоднозначна)
\a, \b, \f, \vЗвуковой сигнал, возврат на символ, перевод страницы, вертикальная табуляция
\eСимвол escape, U+001B (C# 13)

Вывод:

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

Нераспознанная последовательность, например "\d", это ошибка компиляции (CS1009, unrecognized escape sequence), и именно это происходит, когда путь Windows или регулярное выражение записаны как обычная строка.

Verbatim-строки и многострочные строки

Префикс @ перед литералом делает его verbatim (буквальным): обратные слеши становятся обычными символами, а строка может занимать несколько строк. Остаётся только одно экранирование "", которое даёт одну двойную кавычку.

Вывод:

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

Многострочная verbatim-строка включает всё между кавычками, в том числе отступы строк продолжения, поэтому такие строки обычно пишут вплотную к левому краю, как выше. Переводы строк в ней те же, что в исходном файле, поэтому файл, сохранённый с окончаниями строк Windows, даёт \r\n.

C# 11 добавил необработанные строковые литералы (raw string literals), которые решают обе проблемы. Они начинаются и заканчиваются тремя или более двойными кавычками, вообще не требуют экранирования и убирают отступ, равный отступу закрывающих кавычек:

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

Если само содержимое содержит """, начинайте и заканчивайте четырьмя кавычками. Необработанные литералы сочетаются и с интерполяцией ($"""..."""); см. интерполяция строк.

null, пустая строка и пробелы

Строковая переменная может быть null (строки нет вообще), пустой ("", длина 0) или содержать только пробельные символы. Два статических метода проверяют эти случаи без риска получить NullReferenceException:

Вывод:

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

Для проверки пользовательского ввода почти всегда нужен string.IsNullOrWhiteSpace. string.Empty и "" это одно и то же значение; что писать, дело стиля.

Перебор символов

Строка перечисляема, поэтому foreach проходит по каждому char. У типа char есть статические методы для классификации символов:

Вывод:

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

Соединение строк

+ выполняет конкатенацию и работает с любым типом с другой стороны (вызывая ToString()). Для объединения многих значений лучше подходит интерполяция или string.Join:

Вывод:

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

Последние две строки показывают ловушку приоритета: + вычисляется слева направо, поэтому как только левая часть стала строкой, каждый следующий + это конкатенация. Заключайте арифметику внутри конкатенации в скобки.

Часто задаваемые вопросы

Как узнать длину строки в C#?

Используйте свойство Length: "hello".Length равно 5. Это свойство, а не метод, поэтому скобки не нужны. Length считает кодовые единицы UTF-16, поэтому эмодзи или другой символ за пределами базовой многоязычной плоскости считается за 2. Обращение к нему у строки null выбрасывает NullReferenceException.

Как сравнивать строки в C#?

a == b сравнивает текст с учётом регистра. Для сравнения без учёта регистра используйте string.Equals(a, b, StringComparison.OrdinalIgnoreCase). Чтобы сортировать или упорядочивать строки, используйте string.Compare(a, b, StringComparison.Ordinal) или сравнение с учётом культуры, которое возвращает отрицательное число, ноль или положительное число.

Как написать многострочную строку в C#?

Поставьте перед литералом @, чтобы сделать его verbatim: он может занимать несколько строк, обратные слеши в нём буквальные, а двойная кавычка записывается как "". В C# 11 и новее необработанные строковые литералы, ограниченные """, тоже занимают несколько строк, убирают общий отступ и вообще не требуют экранирования. Можно также соединять строки через \n или Environment.NewLine.

Что такое verbatim-строка в C#?

Строковый литерал с префиксом @, например @"C:\Users\ana". Escape-последовательности в нём не обрабатываются, поэтому обратный слеш это просто обратный слеш, что делает такую строку естественным выбором для путей Windows и регулярных выражений. Единственное экранирование внутри неё "" для двойной кавычки.

Чем string отличается от String в C#?

Ничем. string это ключевое слово C#, псевдоним типа .NET System.String, так же как int это псевдоним System.Int32. По соглашению string используют в объявлениях и в статических вызовах вроде string.IsNullOrEmpty(...), но оба варианта компилируются в одно и то же.

Какие escape-последовательности поддерживает C#?

\n перевод строки, \r возврат каретки, \t табуляция, \\ обратный слеш, \" двойная кавычка, \' одинарная кавычка, \0 нулевой символ, \uXXXX символ UTF-16 по шестнадцатеричному коду, \U0001F600 символ по 8-значному коду (превращается в суррогатную пару), \a звуковой сигнал, \b возврат на символ, \f перевод страницы, \v вертикальная табуляция и \e (C# 13) символ escape. Неизвестная последовательность, например \d, это ошибка компиляции (CS1009), поэтому регулярные выражения обычно пишут как verbatim-строки.

Coddy programming languages illustration

Учитесь программировать с Coddy

НАЧАТЬ