Перейти к содержимому
Теория · Строки · урок 3 из 10

Методы строк

С чеком в кассе можно сделать многое: найти на нём строчку с хлебом, перепечатать заголовок крупными буквами, отрезать нижнюю часть с рекламой. Но сам чек кассир не правит. Он печатает новый, уже с нужными изменениями, а старый остаётся таким, каким был.

У строки для таких действий есть готовые методы. Их вызывают через точку после строки, как Length, только со скобками в конце: raw.Trim(). Каждый метод возвращает результат: новую строку, число или bool. Исходная строка при этом остаётся прежней:

C#
string raw = "  Hello, World  ";
string text = raw.Trim();
Console.WriteLine(text);           // Hello, World
Console.WriteLine(text.ToUpper()); // HELLO, WORLD
Console.WriteLine(text.ToLower()); // hello, world
Console.WriteLine(raw.Length);     // 16
Console.WriteLine(text.Length);    // 12

Давайте разберём пример по строкам:

  1. raw.Trim() возвращает копию строки без пробелов по краям, и её кладут в text.
  2. text.ToUpper() возвращает тот же текст заглавными буквами.
  3. text.ToLower() возвращает его строчными буквами.
  4. raw.Length по-прежнему 16: Trim не тронул raw, пробелы по краям остались в нём.
  5. В text 12 символов: Trim не убирает пробел между словами.

Регистр и пробелы

ToUpper и ToLower

ToUpper переводит все буквы в заглавные, а ToLower — в строчные. Цифры, пробелы и знаки препинания они не меняют. Эти методы пригодятся, когда текст нужно привести к одному виду, например перед печатью заголовка.

Trim

Trim убирает пробелы, табуляции и переводы строки в начале и в конце строки. Внутри строки он ничего не трогает. Если лишние пробелы мешают только с одной стороны, есть TrimStart для начала и TrimEnd для конца.

Поиск

IndexOf

IndexOf ищет символ или кусок текста и возвращает индекс первого совпадения. Для text из примера text.IndexOf("World") даёт 7, а text.IndexOf('o') — 4: первая o стоит в слове Hello. Если совпадения нет, метод возвращает -1, как Array.IndexOf у массива.

Поиск учитывает регистр, поэтому text.IndexOf("world") тоже даёт -1.

Contains

Contains отвечает на вопрос попроще: есть ли кусок в строке вообще. text.Contains("World") возвращает true, а text.Contains("world") — false.

Вырезать кусок

Substring возвращает часть строки. У него две формы:

  • Substring(start) — от индекса start до конца строки;
  • Substring(start, length) — кусок длиной length, начиная с индекса start.

Часто их сочетают с IndexOf: сначала находят границу, потом вырезают. Так из адреса почты достают имя и домен:

C#
string email = "anna@mail.com";
int at = email.IndexOf('@');
Console.WriteLine(at);                      // 4
Console.WriteLine(email.Substring(0, at));  // anna
Console.WriteLine(email.Substring(at + 1)); // mail.com
Console.WriteLine(email.Substring(5, 4));   // mail
Console.WriteLine(email.IndexOf('#'));      // -1

Разберём по шагам:

  1. @ стоит на индексе 4, это значение попадает в at.
  2. Substring(0, at) берёт четыре символа с начала: anna.
  3. Substring(at + 1) берёт всё после @: mail.com.
  4. Substring(5, 4) — четыре символа, начиная с индекса 5: mail. Второе число — длина куска, а не индекс конца.
  5. Символа # в адресе нет, поэтому IndexOf возвращает -1.

Здесь чек и строка расходятся. Отрезанный кусок чека из него пропадает, а Substring ничего не отрезает: он копирует часть символов в новую строку, и email остаётся целым.

Кусок может заканчиваться последним символом строки: начало плюс длина могут равняться длине строки. Если сумма больше, Substring останавливает программу с исключением ArgumentOutOfRangeException.

На собеседовании любят спросить, что значит второй аргумент Substring. В C# это длина куска, а у похожего метода в Java и JavaScript — индекс конца. Те, кто пришёл с этих языков, часто путаются.

Заменить

Replace заменяет каждое вхождение одного куска на другой и возвращает новую строку. text.Replace("World", "C#") даёт Hello, C#, а text.Replace('l', 'L') меняет все три буквы: HeLLo, WorLd.

Разбить и собрать

Split

Split режет строку по символу-разделителю и возвращает массив кусков string[]. Сам разделитель в куски не попадает. Два разделителя подряд дают пустой кусок: "a,,b".Split(',') возвращает три строки, и средняя из них — пустая строка "".

string.Join

string.Join вы уже встречали в теме о массивах. Он делает обратное: склеивает элементы массива в одну строку и вставляет между ними разделитель. Этот метод вызывают у самого типа string, а не у конкретной строки: string.Join(", ", parts).

C#
string line = "red,green,blue";
string[] parts = line.Split(',');
Console.WriteLine(parts.Length);              // 3
Console.WriteLine(parts[1]);                  // green
Console.WriteLine(string.Join(" | ", parts)); // red | green | blue

Split(',') дал массив из трёх строк, и parts[1] — вторая из них. string.Join склеил их обратно, но уже через |.

Частая ошибка

Второе число Substring приняли за индекс конца:

C#
string text = "Hello, World";
Console.WriteLine(text.Substring(7, 12)); // ✗
TEXT
Unhandled exception. System.ArgumentOutOfRangeException: Index and length must refer to a location within the string. (Parameter 'length')

Хотели вырезать World: он начинается на индексе 7, а строка кончается перед индексом 12. Но Substring прочитал второе число как длину: 12 символов, начиная с индекса 7. В строке всего 12 символов, и такой кусок вышел бы за её конец.

Программа компилируется, а при запуске останавливается на этой строке. Нужен кусок длиной 5, text.Substring(7, 5), или остаток строки, text.Substring(7).

Главное

  • Методы строки возвращают результат, а исходная строка остаётся прежней: Trim, ToUpper, ToLower, Replace дают новые строки.
  • IndexOf возвращает индекс первого совпадения или -1, Contains — true или false; оба учитывают регистр.
  • Substring(start, length) берёт length символов, начиная со start; второе число — длина, а не индекс конца.
  • Split режет строку в массив по разделителю, string.Join склеивает массив обратно.