Методы строк — полное руководство в C# (.NET): подробное руководство
Что это такое и зачем? (аналогия из реальной жизни)
Представьте, что вы пишете письмо другу. Сначала вы просто набираете текст: «Привет, как дела?». Но потом вы хотите его улучшить: сделать первую букву заглавной, заменить слово «Привет» на «Здравствуйте», проверить, нет ли лишних пробелов, или узнать, сколько в письме символов. Всё это — операции над строкой.
Строка в программировании — это просто последовательность символов: букв, цифр, знаков препинания и пробелов. В C# строки — это не просто набор символов, а специальный тип данных string (с маленькой буквы) или его полное имя System.String.
Методы строк — это встроенные инструменты, которые позволяют вам манипулировать текстом. Вместо того чтобы писать сложные алгоритмы для поиска слова или замены букв, вы просто вызываете готовый метод. Это как иметь под рукой швейцарский нож для работы с текстом: есть ножницы (обрезать), лупа (найти), клей (склеить) и линейка (измерить).
Важно: Строки в C# — неизменяемые (immutable). Это значит, что когда вы вызываете метод, который «меняет» строку, на самом деле создаётся новая строка, а старая остаётся без изменений. Мы подробно разберём это дальше.
Теория: как это работает изнутри
Чтобы понимать, как работают методы строк, нужно заглянуть «под капот» C#.
Тип данных string
В C# строка — это ссылочный тип. Это значит, что переменная хранит не сам текст, а ссылку на область памяти, где этот текст находится. Когда вы пишете:
string имя = "Анна";
…в памяти создаётся объект, который содержит символы 'А', 'н', 'н', 'а'. Переменная имя хранит адрес этого объекта.
Неизменяемость (Immutability)
Это ключевое понятие. Когда вы делаете:
string текст = "Привет";
текст = текст.ToUpper(); // "ПРИВЕТ"
…строка "Привет" не изменилась. Вместо этого в памяти создалась новая строка "ПРИВЕТ", а переменная текст теперь ссылается на неё. Старая строка "Привет" осталась в памяти и будет удалена сборщиком мусора позже.
Почему это сделано? Неизменяемость делает строки безопасными для многопоточности и позволяет оптимизировать память. Например, если у вас 10 переменных содержат слово "Привет", они могут ссылаться на один и тот же объект в памяти, а не создавать 10 копий.
Символы и индексы
Каждый символ в строке имеет свой номер — индекс. Индексация начинается с нуля:
| Символ | П | р | и | в | е | т |
|---|---|---|---|---|---|---|
| Индекс | 0 | 1 | 2 | 3 | 4 | 5 |
Длина строки "Привет" равна 6. Последний символ всегда имеет индекс длина - 1.
Кодировка
Внутри C# строки хранятся в кодировке UTF-16. Это значит, что каждый символ занимает 2 байта (или 4 для редких символов). Для русских букв, английских, цифр — всё работает прозрачно. Но если вы работаете с файлами или сетью, нужно помнить про кодировки (UTF-8, ASCII и т.д.).
Синтаксис в C# (.NET): все формы и варианты
Методы строк делятся на несколько категорий. Мы рассмотрим самые важные.
1. Создание строк
Строки можно создавать разными способами:
string s1 = "Простая строка"; // Самый частый способ
string s2 = "Строка с "кавычками""; // Экранирование кавычек
string s3 = @"Строка с без экранирования"; // Вербатимная строка (сырая)
string s4 = $"Значение: {42}"; // Интерполяция (вставка значений)
string s5 = new string('*', 10); // Строка из 10 звёздочек: "**********"
string s6 = new string(new char[] {'A', 'B', 'C'}); // Из массива символов
2. Основные методы (справочная таблица)
| Метод | Описание | Пример | Результат |
|---|---|---|---|
Length |
Свойство, возвращает длину строки | "Привет".Length |
6 |
ToUpper() |
Преобразует в верхний регистр | "Привет".ToUpper() |
"ПРИВЕТ" |
ToLower() |
Преобразует в нижний регистр | "Привет".ToLower() |
"привет" |
Trim() |
Удаляет пробелы в начале и конце | " Привет ".Trim() |
"Привет" |
TrimStart() |
Удаляет пробелы только в начале | " Привет".TrimStart() |
"Привет" |
TrimEnd() |
Удаляет пробелы только в конце | "Привет ".TrimEnd() |
"Привет" |
Substring(int startIndex) |
Извлекает подстроку с указанного индекса до конца | "Привет".Substring(2) |
"ивет" |
Substring(int startIndex, int length) |
Извлекает подстроку заданной длины | "Привет".Substring(2, 3) |
"иве" |
IndexOf(string value) |
Находит индекс первого вхождения подстроки | "Привет мир".IndexOf("мир") |
7 |
LastIndexOf(string value) |
Находит индекс последнего вхождения | "мир мир".LastIndexOf("мир") |
4 |
Contains(string value) |
Проверяет, содержит ли строка подстроку | "Привет".Contains("иве") |
true |
StartsWith(string value) |
Проверяет, начинается ли строка с подстроки | "Привет".StartsWith("При") |
true |
EndsWith(string value) |
Проверяет, заканчивается ли строка на подстроку | "Привет".EndsWith("ет") |
true |
Replace(string oldValue, string newValue) |
Заменяет все вхождения одной подстроки на другую | "Привет мир".Replace("мир", "друг") |
"Привет друг" |
Split(params char[] separator) |
Разбивает строку на массив подстрок по разделителю | "один,два,три".Split(',') |
["один", "два", "три"] |
Join(string separator, string[] values) |
Статический метод, объединяет массив строк с разделителем | string.Join(", ", ["a", "b", "c"]) |
"a, b, c" |
ToCharArray() |
Преобразует строку в массив символов | "Привет".ToCharArray() |
['П', 'р', 'и', 'в', 'е', 'т'] |
IsNullOrEmpty() |
Статический метод, проверяет, null или пустая строка | string.IsNullOrEmpty("") |
true |
IsNullOrWhiteSpace() |
Проверяет, null, пустая или состоит только из пробелов | string.IsNullOrWhiteSpace(" ") |
true |
3. Сравнение строк
Сравнивать строки можно через ==, но для точного контроля используйте метод Equals или Compare:
string a = "Привет";
string b = "привет";
// Простое сравнение (учёт регистра)
bool равно1 = a == b; // false
// Сравнение без учёта регистра
bool равно2 = a.Equals(b, StringComparison.OrdinalIgnoreCase); // true
// Сравнение с учётом культуры (для сортировки)
int результат = string.Compare("Анна", "Борис", StringComparison.CurrentCulture);
// результат < 0, так как "Анна" идёт раньше "Бориса"
Важно: Для проверки на пустоту или null всегда используйтеstring.IsNullOrEmpty()илиstring.IsNullOrWhiteSpace(). Никогда не сравнивайте с""напрямую — это может привести к ошибке, если строка равнаnull.
Примеры кода — от простого к сложному
Теперь давайте посмотрим на методы в действии. Все примеры рабочие, их можно скопировать и запустить в консольном приложении C#.
Пример 1: Приветствие пользователя (базовые методы)
using System;
class Program
{
static void Main()
{
// Просим пользователя ввести имя
Console.Write("Введите ваше имя: ");
string имя = Console.ReadLine(); // Читаем строку с консоли
// Удаляем лишние пробелы и делаем первую букву заглавной
имя = имя.Trim(); // Убираем пробелы в начале и конце
// Проверяем, не пустое ли имя
if (string.IsNullOrEmpty(имя))
{
Console.WriteLine("Вы не ввели имя!");
return;
}
// Делаем первую букву заглавной, остальные — маленькими
string перваяБуква = имя.Substring(0, 1).ToUpper(); // Берём первый символ и делаем его большим
string остальные = имя.Substring(1).ToLower(); // Остальные — маленькими
имя = перваяБуква + остальные; // Склеиваем
// Выводим приветствие
Console.WriteLine($"Привет, {имя}! Добро пожаловать в мир C#.");
// Показываем длину имени
Console.WriteLine($"Твоё имя состоит из {имя.Length} букв.");
}
}
Объяснение:
Console.ReadLine()— читает строку, введённую пользователем.Trim()— удаляет лишние пробелы (пользователь мог случайно нажать пробел).string.IsNullOrEmpty()— проверяет, не пустая ли строка.Substring(0, 1)— берёт первый символ (индекс 0, длина 1).ToUpper()иToLower()— меняют регистр.Length— свойство, возвращает количество символов.
Пример 2: Поиск и замена в тексте
using System;
class Program
{
static void Main()
{
string текст = "C# — это лучший язык программирования! C# очень мощный.";
// Проверяем, содержит ли текст слово "C#"
if (текст.Contains("C#"))
{
Console.WriteLine("Текст содержит упоминание C#.");
}
// Находим позицию первого вхождения "C#"
int позиция = текст.IndexOf("C#");
Console.WriteLine($"Первое вхождение 'C#' на индексе: {позиция}");
// Заменяем все вхождения "C#" на "C Sharp"
string новыйТекст = текст.Replace("C#", "C Sharp");
Console.WriteLine($"После замены: {новыйТекст}");
// Проверяем, начинается ли текст с "C#"
if (текст.StartsWith("C#"))
{
Console.WriteLine("Текст начинается с C#.");
}
// Проверяем, заканчивается ли текст на "мощный."
if (текст.EndsWith("мощный."))
{
Console.WriteLine("Текст заканчивается на 'мощный.'");
}
}
}
Объяснение:
Contains()— возвращаетtrue, если подстрока найдена.IndexOf()— возвращает индекс первого вхождения (или -1, если не найдено).Replace()— заменяет все вхождения. Помните: создаётся новая строка!StartsWith()иEndsWith()— проверяют начало и конец строки.
Пример 3: Разбиение и объединение строк
using System;
class Program
{
static void Main()
{
// Исходные данные: список имён через запятую
string данные = "Анна, Борис, Виктор, Галина, Дмитрий";
// Разбиваем строку на массив по запятой и пробелу
// Split принимает массив разделителей
string[] имена = данные.Split(new char[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries);
Console.WriteLine("Список имён:");
foreach (string имя in имена)
{
Console.WriteLine($"- {имя}");
}
// Теперь объединяем массив обратно, но с другим разделителем
string объединённые = string.Join(" | ", имена);
Console.WriteLine($"Объединённые: {объединённые}");
// Ещё один пример: разбиваем предложение на слова
string предложение = "C# — отличный язык для начинающих";
string[] слова = предложение.Split(' ');
Console.WriteLine($"Количество слов: {слова.Length}");
}
}
Объяснение:
Split()— разбивает строку на массив. Можно указать несколько разделителей.StringSplitOptions.RemoveEmptyEntries— удаляет пустые элементы (если есть два разделителя подряд).string.Join()— статический метод, объединяет массив в строку с указанным разделителем.
Пример 4: Работа с пробелами и форматированием
using System;
class Program
{
static void Main()
{
string грязнаяСтрока = " Привет, мир! ";
Console.WriteLine($"Исходная: '{грязнаяСтрока}'");
Console.WriteLine($"Длина: {грязнаяСтрока.Length}");
// Trim — удаляет пробелы в начале и конце
string обрезанная = грязнаяСтрока.Trim();
Console.WriteLine($"После Trim: '{обрезанная}'");
// TrimStart — только в начале
string безНачала = грязнаяСтрока.TrimStart();
Console.WriteLine($"После TrimStart: '{безНачала}'");
// TrimEnd — только в конце
string безКонца = грязнаяСтрока.TrimEnd();
Console.WriteLine($"После TrimEnd: '{безКонца}'");
// PadLeft и PadRight — добавляют пробелы до нужной длины
string имя = "Анна";
string выровненное = имя.PadLeft(10); // Добавляет пробелы слева до длины 10
Console.WriteLine($"Выровненное вправо: '{выровненное}'");
string выровненное2 = имя.PadRight(10, '-'); // Заполняет справа символами '-'
Console.WriteLine($"С заполнением: '{выровненное2}'");
}
}
Объяснение:
Trim(),TrimStart(),TrimEnd()— удаляют пробелы (или другие символы, если передать их в параметре).PadLeft()иPadRight()— добавляют символы до указанной длины. Полезно для форматирования таблиц.
Пример 5: Проверка и валидация данных
using System;
class Program
{
static void Main()
{
// Проверяем email на корректность (упрощённо)
string email = "user@example.com";
// Проверяем, что есть '@' и '.'
if (email.Contains("@") && email.Contains("."))
{
// Находим позицию '@'
int позицияСобаки = email.IndexOf("@");
// Берём часть после '@'
string домен = email.Substring(позицияСобаки + 1);
if (домен.Contains("."))
{
Console.WriteLine("Email выглядит корректно.");
}
else
{
Console.WriteLine("В домене должна быть точка.");
}
}
else
{
Console.WriteLine("Email должен содержать '@' и '.'");
}
// Проверяем, является ли строка числом
string возможноеЧисло = "12345";
if (int.TryParse(возможноеЧисло, out int число))
{
Console.WriteLine($"Это число: {число}");
}
else
{
Console.WriteLine("Это не число.");
}
// Проверка на пустоту и пробелы
string пустая = " ";
if (string.IsNullOrWhiteSpace(пустая))
{
Console.WriteLine("Строка пустая или состоит из пробелов.");
}
}
}
Объяснение:
Contains()иIndexOf()используются для проверки структуры email.Substring()— извлекает часть строки после символа '@'.int.TryParse()— пытается преобразовать строку в число. Возвращаетtrueпри успехе.IsNullOrWhiteSpace()— лучший способ проверить, что строка не содержит значимых символов.
Пример 6: Извлечение данных из строки (парсинг)
using System;
class Program
{
static void Main()
{
// Допустим, у нас есть строка с данными о человеке
string данные = "Иван Петров, 25 лет, город Москва";
// Разбиваем по запятой
string[] части = данные.Split(',');
// Первая часть — имя и фамилия
string имяФамилия = части[0].Trim();
Console.WriteLine($"Имя и фамилия: {имяФамилия}");
// Вторая часть — возраст
string возрастСтрока = части[1].Trim();
// Убираем слово "лет" и пробелы
возрастСтрока = возрастСтрока.Replace("лет", "").Trim();
int возраст = int.Parse(возрастСтрока); // Преобразуем в число
Console.WriteLine($"Возраст: {возраст}");
// Третья часть — город
string город = части[2].Trim();
// Убираем слово "город"
город = город.Replace("город", "").Trim();
Console.WriteLine($"Город: {город}");
// Альтернативный способ: используем Substring и IndexOf
string текст = "ID: 12345, Имя: Анна";
int старт = текст.IndexOf("ID: ") + 4; // +4, чтобы пропустить "ID: "
int конец = текст.IndexOf(",", старт);
string id = текст.Substring(старт, конец - старт).Trim();
Console.WriteLine($"Извлечённый ID: {id}");
}
}
Объяснение:
- Парсинг — это извлечение структурированных данных из строки.
- Комбинируем
Split(),Trim(),Replace()для очистки данных. int.Parse()— преобразует строку в число (выбросит исключение, если строка не число).- Второй способ показывает, как использовать
IndexOf()иSubstring()для точного извлечения.
Пример 7: Работа с большим текстом и StringBuilder
using System;
using System.Text; // Нужно для StringBuilder
class Program
{
static void Main()
{
// Проблема: если много раз склеивать строки через '+', это медленно
// Потому что каждая операция создаёт новую строку
string текст = "";
for (int i = 0; i < 10; i++)
{
текст += i.ToString() + " "; // Плохо для больших циклов!
}
Console.WriteLine($"Через '+': {текст}");
// Решение: использовать StringBuilder
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 10; i++)
{
sb.Append(i);
sb.Append(" ");
}
string результат = sb.ToString(); // Преобразуем в обычную строку
Console.WriteLine($"Через StringBuilder: {результат}");
// Другие методы StringBuilder
sb.Clear(); // Очищаем
sb.Append("Привет");
sb.AppendLine(); // Добавляет новую строку
sb.Append("Мир");
Console.WriteLine($"Многострочный текст:
{sb.ToString()}");
// Вставка и замена в StringBuilder
sb.Insert(0, "Начало: "); // Вставляем в начало
sb.Replace("Мир", "C#"); // Заменяем подстроку
Console.WriteLine($"После изменений: {sb.ToString()}");
}
}
Объяснение:
StringBuilder— это изменяемая строка. Она не создаёт новые объекты при каждом изменении.- Используйте
StringBuilder, когда нужно много раз менять строку (в циклах, при построении больших текстов). Append()— добавляет текст в конец.AppendLine()— добавляет текст и перевод строки.Insert()— вставляет текст в указанную позицию.Replace()— заменяет подстроку.ToString()— преобразует StringBuilder обратно в обычную строку.
Сравнение с Python: чем похоже и чем отличается
Если вы уже знакомы с Python, вот ключевые различия и сходства:
| Операция | C# | Python | Примечание |
|---|---|---|---|
| Длина строки | str.Length |
len(str) |
В C# это свойство, в Python — функция |
| Верхний/нижний регистр | str.ToUpper(), str.ToLower() |
str.upper(), str.lower() |
Почти одинаково, но названия методов разные |
| Удаление пробелов | str.Trim(), TrimStart(), TrimEnd() |
str.strip(), str.lstrip(), str.rstrip() |
Аналогично, но названия отличаются |
| Разбиение | str.Split(',') |
str.split(',') |
Почти одинаково, но Split в C# возвращает массив, в Python — список |
| Объединение | string.Join(",", array) |
",".join(list) |
В C# метод вызывается на классе string, в Python — на разделителе |
| Замена | str.Replace("old", "new") |
str.replace("old", "new") |
Почти одинаково |
| Поиск подстроки | str.IndexOf("sub") |
str.find("sub") |
Оба возвращают индекс или -1 |
| Проверка вхождения | str.Contains("sub") |
"sub" in str |
В Python используется оператор in |
| Срезы (подстроки) | str.Substring(start, length) |
str[start:end] |
В C# нужно указывать длину, в Python — конечный индекс |
| Форматирование | $"Значение: {var}" |
f"Значение: {var}" |
Очень похожий синтаксис интерполяции |
| Неизменяемость | Да, строки immutable | Да, строки immutable | Оба языка делают строки неизменяемыми |
| StringBuilder | Есть StringBuilder |
Нет встроенного, но можно использовать str.join() или io.StringIO |
В C# это отдельный класс для эффективной работы |
Главное отличие: В C# строгость типов. Вы не можете просто так смешать строку и число — нужно явно преобразовывать. В Python это делается автоматически. Также в C# больше внимания уделяется производительности (StringBuilder) и безопасности (проверки на null).
Типичные ошибки и подводные камни
Ошибка 1: Забываем про неизменяемость
string текст = "Привет";
текст.ToUpper(); // Ошибка! Результат не сохранён
Console.WriteLine(текст); // Выведет "Привет"
Правильно: текст = текст.ToUpper();
Ошибка 2: Использование == для сравнения с null
string s = null;
if (s == "") // Ошибка! Сравнение с null даст false, но лучше использовать IsNullOrEmpty
{
// ...
}