Методы строк — полное руководство в C++: подробное руководство
Что это такое и зачем? (аналогия из реальной жизни)
Представьте, что вы работаете в библиотеке. У вас есть книга — это строка текста. Но книга может быть в разных состояниях: новая, старая, с пометками, на разных языках. Вы можете с ней делать разные действия: переписать её от руки, найти в ней определённое слово, заменить одно слово на другое, перевести в верхний регистр (заглавные буквы), разрезать на части и склеить обратно.
В программировании строка — это последовательность символов. Это может быть имя пользователя, пароль, адрес электронной почты, целое предложение или даже целая книга. Методы строк — это встроенные инструменты, которые позволяют нам манипулировать этими текстовыми данными: искать, заменять, изменять регистр, обрезать лишние пробелы, сравнивать и многое другое.
Аналогия: Представьте, что строка — это пластилиновая колбаска. Методы строк — это ваши руки и инструменты: вы можете отрезать кусочек (подстрока), прилепить другой кусочек (конкатенация), разгладить (изменить регистр) или найти, где находится определённый цветной вкрапление (поиск подстроки).
Зачем это нужно? Представьте, что вы пишете программу для регистрации пользователей. Вам нужно:
- Проверить, что email содержит символ '@' (поиск)
- Убрать лишние пробелы в начале и конце имени (обрезка)
- Сделать первую букву имени заглавной (изменение регистра)
- Заменить все точки в номере телефона на дефисы (замена)
Без методов строк вам пришлось бы писать десятки строк кода, чтобы сделать эти простые операции. Методы строк экономят ваше время и делают код понятным и надёжным.
Теория: как это работает изнутри
В C++ есть два основных типа строк: std::string (из библиотеки <string>) и старые C-строки (массивы символов char[]). Мы будем изучать std::string, так как это современный, безопасный и удобный способ работы с текстом.
Внутри std::string — это объект, который хранит массив символов (char) и дополнительную информацию: длину строки и ёмкость (сколько памяти выделено). Когда вы вызываете метод, вы обращаетесь к этому объекту и просите его выполнить действие.
Важно понимать: строка в C++ — это изменяемый объект. Это значит, что когда вы вызываете метод, который изменяет строку (например, replace или append), строка меняется на месте. Это отличается от некоторых других языков, где строки неизменяемы (immutable).
Каждый символ в строке имеет свой индекс (позицию). Индексация начинается с 0. Например, в строке "Привет" символ 'П' имеет индекс 0, 'р' — 1, 'и' — 2 и так далее.
Важно: В C++ строки могут содержать любые символы, включая пробелы, цифры, знаки препинания и символы Unicode (если использоватьstd::wstringилиstd::u32string). Но для простоты мы будем работать с обычными строкамиstd::string, которые хорошо работают с латиницей и кириллицей в кодировке UTF-8.
Синтаксис в C++: все формы и варианты
Чтобы использовать строки, нужно подключить библиотеку:
#include <iostream>
#include <string> // Подключаем библиотеку для работы со строками
int main() {
// Создание строки
std::string name = "Анна";
std::string greeting("Привет, мир!");
std::string empty; // Пустая строка
// Вызов метода — через точку
std::cout << name.length() << std::endl; // Выведет: 4
std::cout << greeting.size() << std::endl; // Выведет: 12
return 0;
}
Теперь рассмотрим все основные методы std::string в виде справочной таблицы:
| Метод | Описание | Пример вызова | Результат |
|---|---|---|---|
length() / size() |
Возвращает длину строки (количество символов) | "Привет".length() |
6 |
empty() |
Проверяет, пуста ли строка (возвращает true/false) | "".empty() |
true |
clear() |
Очищает строку (делает её пустой) | str.clear() |
строка становится "" |
at(index) |
Возвращает символ по индексу (с проверкой границ) | "Кот".at(1) |
'о' |
operator[] |
Возвращает символ по индексу (без проверки границ) | "Кот"[1] |
'о' |
front() |
Возвращает первый символ | "Кот".front() |
'К' |
back() |
Возвращает последний символ | "Кот".back() |
'т' |
append(str) |
Добавляет строку в конец (аналог +=) | "При".append("вет") |
"Привет" |
insert(pos, str) |
Вставляет строку в указанную позицию | "Привет".insert(3, "ку") |
"Прикувет" |
erase(pos, count) |
Удаляет count символов, начиная с позиции pos | "Привет".erase(2, 2) |
"Пвет" |
replace(pos, count, str) |
Заменяет count символов на строку str | "Привет".replace(2, 2, "ка") |
"Пкавет" |
substr(pos, count) |
Извлекает подстроку длиной count, начиная с pos | "Привет".substr(2, 3) |
"иве" |
find(str) |
Ищет подстроку, возвращает индекс первого вхождения | "Привет".find("иве") |
2 |
rfind(str) |
Ищет подстроку справа налево | "Привет".rfind("е") |
4 |
find_first_of(str) |
Ищет первое вхождение любого символа из str | "Привет".find_first_of("ае") |
4 (символ 'е') |
find_last_of(str) |
Ищет последнее вхождение любого символа из str | "Привет".find_last_of("ае") |
4 |
compare(str) |
Сравнивает строки (возвращает 0, если равны) | "Кот".compare("Кот") |
0 |
c_str() |
Преобразует в C-строку (массив char) | "Привет".c_str() |
const char* |
data() |
Возвращает указатель на внутренние данные | "Привет".data() |
const char* |
push_back(ch) |
Добавляет один символ в конец | str.push_back('!') |
строка + '!' |
pop_back() |
Удаляет последний символ | str.pop_back() |
строка без последнего символа |
resize(n) |
Изменяет размер строки до n символов | "Привет".resize(3) |
"При" |
reserve(n) |
Резервирует память для n символов | str.reserve(100) |
выделяет память |
capacity() |
Возвращает текущую ёмкость строки | str.capacity() |
число |
shrink_to_fit() |
Уменьшает ёмкость до размера строки | str.shrink_to_fit() |
оптимизация памяти |
starts_with(str) (C++20) |
Проверяет, начинается ли строка с подстроки | "Привет".starts_with("При") |
true |
ends_with(str) (C++20) |
Проверяет, заканчивается ли строка на подстроку | "Привет".ends_with("вет") |
true |
contains(str) (C++23) |
Проверяет, содержит ли строка подстроку | "Привет".contains("иве") |
true |
Примечание: Методыstarts_with,ends_withиcontainsдоступны только в стандартах C++20 и C++23 соответственно. Если вы используете старый компилятор, эти методы могут быть недоступны.
Примеры кода — от простого к сложному
Пример 1: Базовые операции — длина, доступ к символам, сравнение
#include <iostream>
#include <string>
int main() {
std::string text = "Программирование";
// Получаем длину строки
int length = text.length();
std::cout << "Длина строки: " << length << std::endl;
// Доступ к символам по индексу
char firstChar = text[0]; // 'П'
char secondChar = text.at(1); // 'р' (at() безопаснее)
char lastChar = text.back(); // 'е'
std::cout << "Первый символ: " << firstChar << std::endl;
std::cout << "Второй символ: " << secondChar << std::endl;
std::cout << "Последний символ: " << lastChar << std::endl;
// Сравнение строк
std::string str1 = "Кот";
std::string str2 = "Кот";
std::string str3 = "кот"; // Разный регистр
if (str1 == str2) {
std::cout << "str1 и str2 равны" << std::endl;
}
if (str1.compare(str3) != 0) {
std::cout << "str1 и str3 НЕ равны (разный регистр)" << std::endl;
}
return 0;
}
Объяснение: В этом примере мы создаём строку "Программирование" и изучаем её длину. Затем получаем доступ к символам разными способами: через оператор [] (быстро, но без проверки) и через метод at() (безопаснее, выбрасывает исключение при выходе за границы). В конце сравниваем строки: оператор == удобен для простой проверки на равенство, а compare() даёт больше информации (отрицательное число, если первая строка меньше, положительное — если больше).
Пример 2: Поиск подстроки и проверка наличия
#include <iostream>
#include <string>
int main() {
std::string sentence = "Быстрая коричневая лиса прыгает через ленивую собаку";
// Ищем слово "лиса"
size_t found = sentence.find("лиса");
if (found != std::string::npos) {
std::cout << "Слово 'лиса' найдено на позиции: " << found << std::endl;
} else {
std::cout << "Слово 'лиса' не найдено" << std::endl;
}
// Ищем с определённой позиции (после 20-го символа)
size_t found2 = sentence.find("а", 20);
if (found2 != std::string::npos) {
std::cout << "Буква 'а' после 20-й позиции найдена на: " << found2 << std::endl;
}
// Ищем справа налево
size_t lastA = sentence.rfind("а");
std::cout << "Последняя буква 'а' на позиции: " << lastA << std::endl;
// Проверяем, начинается ли строка с определённого слова (C++20)
// if (sentence.starts_with("Быстрая")) {
// std::cout << "Строка начинается с 'Быстрая'" << std::endl;
// }
// Проверяем, содержит ли строка подстроку (C++23)
// if (sentence.contains("собаку")) {
// std::cout << "Строка содержит 'собаку'" << std::endl;
// }
return 0;
}
Объяснение: Метод find() ищет подстроку слева направо и возвращает индекс первого вхождения. Если подстрока не найдена, возвращается специальное значение std::string::npos (обычно -1). Мы всегда должны проверять результат поиска на npos, иначе программа может работать некорректно. rfind() ищет справа налево — полезно, когда нужно найти последнее вхождение.
Пример 3: Извлечение подстроки и замена
#include <iostream>
#include <string>
int main() {
std::string url = "https://example.com/images/photo.jpg";
// Извлекаем подстроку: протокол (первые 5 символов)
std::string protocol = url.substr(0, 5);
std::cout << "Протокол: " << protocol << std::endl;
// Извлекаем домен (с 8-го символа, 11 символов)
std::string domain = url.substr(8, 11);
std::cout << "Домен: " << domain << std::endl;
// Извлекаем всё после последнего слеша
size_t lastSlash = url.rfind("/");
std::string filename = url.substr(lastSlash + 1);
std::cout << "Имя файла: " << filename << std::endl;
// Заменяем расширение файла
std::string newUrl = url;
size_t dotPos = newUrl.rfind(".");
newUrl.replace(dotPos, 4, ".png");
std::cout << "Новый URL: " << newUrl << std::endl;
// Вставляем слово
std::string text = "Привет мир";
text.insert(7, "красивый ");
std::cout << "После вставки: " << text << std::endl;
// Удаляем часть строки
text.erase(7, 9); // Удаляем "красивый "
std::cout << "После удаления: " << text << std::endl;
return 0;
}
Объяснение: substr() — один из самых полезных методов. Он позволяет вырезать любую часть строки. Если не указать второй параметр, подстрока берётся до конца строки. replace() заменяет указанное количество символов на новую строку. insert() вставляет строку в указанную позицию, а erase() удаляет символы. Обратите внимание: все эти методы изменяют исходную строку.
Пример 4: Работа с пробелами и очистка
#include <iostream>
#include <string>
int main() {
std::string dirty = " Привет, мир!
";
std::cout << "Исходная строка: '" << dirty << "'" << std::endl;
std::cout << "Длина: " << dirty.length() << std::endl;
// Удаляем пробелы в начале и конце (вручную, так как нет trim в C++)
// Находим первый непробельный символ
size_t start = dirty.find_first_not_of("
");
// Находим последний непробельный символ
size_t end = dirty.find_last_not_of("
");
if (start != std::string::npos && end != std::string::npos) {
std::string trimmed = dirty.substr(start, end - start + 1);
std::cout << "После обрезки: '" << trimmed << "'" << std::endl;
std::cout << "Длина после обрезки: " << trimmed.length() << std::endl;
}
// Очищаем строку
dirty.clear();
std::cout << "После clear(): '" << dirty << "'" << std::endl;
std::cout << "Пустая? " << (dirty.empty() ? "да" : "нет") << std::endl;
return 0;
}
Объяснение: В C++ нет встроенного метода trim() (как в Python или Java), поэтому мы реализуем его вручную. find_first_not_of() находит первый символ, который не входит в указанный набор (пробелы, табуляция, перевод строки). find_last_not_of() делает то же самое, но с конца. Затем мы вырезаем подстроку между этими позициями. clear() полностью очищает строку, а empty() проверяет, пуста ли она.
Пример 5: Добавление символов и строк
#include <iostream>
#include <string>
int main() {
std::string result = "Результат: ";
// Добавляем строку через append
result.append("5 + 3 = ");
// Добавляем число (преобразуем в строку)
int sum = 5 + 3;
result.append(std::to_string(sum));
std::cout << result << std::endl;
// Используем оператор += (более удобно)
std::string message = "Hello";
message += " ";
message += "World";
message += "!";
std::cout << message << std::endl;
// Добавляем отдельные символы
std::string word = "Програм";
word.push_back('м');
word.push_back('а');
std::cout << "После push_back: " << word << std::endl;
// Удаляем последний символ
word.pop_back();
std::cout << "После pop_back: " << word << std::endl;
// Собираем строку из символов
std::string built;
built += 'C';
built += '+';
built += '+';
built += ' ';
built += '1';
built += '7';
std::cout << "Собрано: " << built << std::endl;
return 0;
}
Объяснение: append() и оператор += — два способа добавить строку в конец. += обычно удобнее и читается лучше. push_back() добавляет один символ, а pop_back() удаляет последний. Обратите внимание на std::to_string() — это функция, которая преобразует число в строку. Это очень полезно, когда нужно соединить текст с числом.
Пример 6: Поиск всех вхождений подстроки
#include <iostream>
#include <string>
#include <vector>
int main() {
std::string text = "Кот в мешке, кот на крыше, кот под диваном";
std::string searchWord = "кот";
std::vector<size_t> positions;
size_t pos = text.find(searchWord);
// Ищем все вхождения в цикле
while (pos != std::string::npos) {
positions.push_back(pos);
// Начинаем поиск со следующей позиции после найденной
pos = text.find(searchWord, pos + 1);
}
std::cout << "Слово '" << searchWord << "' найдено "
<< positions.size() << " раз(а):" << std::endl;
for (size_t i = 0; i < positions.size(); i++) {
std::cout << " Позиция " << (i + 1) << ": " << positions[i] << std::endl;
}
// Альтернативный способ: ищем без учёта регистра
// (преобразуем всё в нижний регистр)
std::string lowerText = text;
for (char &c : lowerText) {
c = std::tolower(c);
}
std::vector<size_t> positionsCaseInsensitive;
pos = lowerText.find(searchWord);
while (pos != std::string::npos) {
positionsCaseInsensitive.push_back(pos);
pos = lowerText.find(searchWord, pos + 1);
}
std::cout << "С учётом регистра: " << positions.size() << " вхождений" << std::endl;
std::cout << "Без учёта регистра: " << positionsCaseInsensitive.size() << " вхождений" << std::endl;
return 0;
}
Объяснение: Чтобы найти все вхождения подстроки, мы используем цикл while. Каждый раз, когда find() находит совпадение, мы запоминаем позицию и начинаем поиск со следующей позиции (pos + 1). Цикл продолжается, пока find() не вернёт npos. Для поиска без учёта регистра мы сначала преобразуем всю строку в нижний регистр с помощью цикла и функции std::tolower().
Пример 7: Разделение строки на части (split)
#include <iostream>
#include <string>
#include <vector>
#include <sstream> // Для std::istringstream
int main() {
std::string data = "яблоко,груша,банан,апельсин,виноград";
char delimiter = ',';
std::vector<std::string> fruits;
std::string current;
// Разделяем строку вручную
for (char c : data) {
if (c == delimiter) {
// Если нашли разделитель, добавляем накопленное слово
fruits.push_back(current);
current.clear(); // Очищаем для следующего слова
} else {
// Иначе добавляем символ к текущему слову
current += c;
}
}
// Добавляем последнее слово (после последнего разделителя)
fruits.push_back(current);
std::cout << "Фрукты:" << std::endl;
for (const std::string& fruit : fruits) {
std::cout << " - " << fruit << std::endl;
}
// Альтернативный способ с использованием stringstream
std::string sentence = "Быстрая коричневая лиса";
std::istringstream stream(sentence);
std::string word;
std::vector<std::string> words;
while (stream >> word) {
words.push_back(word);
}
std::cout << "Слова из предложения:" << std::endl;
for (const std::string& w : words) {
std::cout << " - " << w << std::endl;
}
return 0;
}
Объяснение: В C++ нет встроенного метода split() (как в Python), поэтому мы реализуем его сами. Первый способ — проходим по каждому символу строки. Когда встречаем разделитель (запятую), сохраняем накопленное слово в вектор и очищаем буфер. После цикла не забываем добавить последнее слово. Второй способ использует std::istringstream — это поток, который читает слова, разделённые пробелами. Оператор >> автоматически пропускает пробелы и возвращает следующее слово.
Сравнение с Python: чем похоже и чем отличается
Python и C++ — два очень популярных языка, и работа со строками в них имеет как сходства, так и различия. Давайте сравним их в таблице:
| Операция | C++ (std::string) | Python (str) | Примечания |
|---|---|---|---|
| Изменяемость | Изменяемый (mutable) | Неизменяемый (immutable) | В Python любое изменение создаёт новую строку |
| Длина | str.length() или str.size() |
len(str) |
В C++ это метод, в Python — функция |
| Индексация | str[i] или
|