Как перебрать слова в строке на C++: разбор популярных методов
Решение с использованием std::stringstream и std::string::find_first_of
В C++ нет встроенного метода `split()`, как в Python, но задачу можно решить элегантно с помощью стандартных средств. Рассмотрим два подхода: базовый (с ручной очисткой пунктуации) и продвинутый (с использованием алгоритмов STL).
1. Базовый подход: std::stringstream + фильтрация
Простой и понятный способ для начинающих. Разбиваем строку на слова по пробелам, затем удаляем знаки препинания из каждого слова.
#include <iostream>
#include <sstream>
#include <string>
#include <cctype> // для ispunct
// Функция удаления знаков препинания из строки
std::string cleanWord(const std::string& word) {
std::string result;
for (char ch : word) {
if (!std::ispunct(static_cast<unsigned char>(ch))) {
result += ch;
}
}
return result;
}
int main() {
std::string text = "Привет, как дела? У меня всё хорошо!";
std::istringstream iss(text);
std::string word;
while (iss >> word) {
std::string cleaned = cleanWord(word);
if (!cleaned.empty()) {
std::cout << cleaned << std::endl;
}
}
return 0;
}
Результат:
Привет как дела У меня всё хорошо
Оператор >> для std::istringstream автоматически пропускает пробелы и табуляции, но не убирает пунктуацию. Поэтому мы чистим каждое слово вручную через std::ispunct.
2. Продвинутый подход: разбиение по любым разделителям
Этот метод использует std::string::find_first_of и std::string::find_first_not_of для поиска границ слов по набору разделителей. Он работает быстрее и гибче.
#include <iostream>
#include <string>
#include <vector>
std::vector<std::string> split(const std::string& str, const std::string& delimiters) {
std::vector<std::string> tokens;
size_t start = 0;
size_t end = 0;
while ((start = str.find_first_not_of(delimiters, end)) != std::string::npos) {
end = str.find_first_of(delimiters, start);
tokens.push_back(str.substr(start, end - start));
}
return tokens;
}
int main() {
std::string text = "Привет, как дела? У меня всё хорошо!";
std::string delimiters = " ,.!?;:\t\n"; // разделители: пробел, знаки препинания, табуляция
auto words = split(text, delimiters);
for (const auto& word : words) {
std::cout << word << std::endl;
}
return 0;
}
3. Современный C++17: std::string_view
Для оптимизации можно использовать std::string_view, который не копирует данные, а создаёт представление над исходной строкой:
#include <iostream>
#include <string>
#include <string_view>
#include <vector>
std::vector<std::string_view> splitSV(std::string_view str, std::string_view delimiters) {
std::vector<std::string_view> tokens;
size_t start = 0;
size_t end = 0;
while ((start = str.find_first_not_of(delimiters, end)) != std::string_view::npos) {
end = str.find_first_of(delimiters, start);
tokens.push_back(str.substr(start, end - start));
}
return tokens;
}
int main() {
std::string text = "Привет, как дела? У меня всё хорошо!";
auto words = splitSV(text, " ,.!?;:\t\n");
for (auto word : words) {
std::cout << word << std::endl;
}
return 0;
}
Что выбрать?
- Для обучения и простоты — первый вариант с
std::stringstream. - Для производительности и гибкости — второй вариант с
find_first_of. - Для современного C++ — третий вариант с
std::string_view(C++17+).
Все три подхода корректно обрабатывают множественные пробелы, табуляции и пустые строки. Выбирайте под свою задачу!
<<Альтернатива: использование boost::split или std::regex
Если вы не ограничены стандартной библиотекой и можете использовать Boost, то задача упрощается до одной строки:
#include <boost/algorithm/string.hpp>
#include <iostream>
#include <string>
#include <vector>
int main() {
std::string text = "Привет, как дела? У меня всё хорошо!";
std::vector<std::string> words;
boost::split(words, text, boost::is_any_of(" ,.!?;:\t\n"), boost::token_compress_on);
for (const auto& word : words) {
if (!word.empty()) {
std::cout << word << std::endl;
}
}
return 0;
}
boost::token_compress_on автоматически объединяет последовательные разделители, что избавляет от пустых слов.
Решение через std::regex
Ещё один способ — использовать регулярные выражения из C++11:
#include <iostream>
#include <string>
#include <regex>
#include <iterator>
int main() {
std::string text = "Привет, как дела? У меня всё хорошо!";
std::regex word_regex(R"(\w+)"); // ищем последовательности букв и цифр
auto words_begin = std::sregex_iterator(text.begin(), text.end(), word_regex);
auto words_end = std::sregex_iterator();
for (auto it = words_begin; it != words_end; ++it) {
std::cout << it->str() << std::endl;
}
return 0;
}
Минусы: std::regex может быть медленнее для больших строк, и он не всегда корректно обрабатывает Unicode (например, русские буквы). Для английского текста — отличный вариант.
Итог: Если проект уже использует Boost — берите boost::split. Если хотите универсальности — используйте чистый C++ с find_first_of. Для простых задач — std::stringstream.