Простое разделение строк с istringstream
Это простейший способ разделения строки — пробелом:
#include <iostream>
#include <sstream>
#include <vector>
#include <string>
std::vector<std::string> split(const std::string& str) {
std::vector<std::string> tokens;
std::istringstream iss(str);
std::string token;
while (iss >> token) {
tokens.push_back(token);
}
return tokens;
}
int main() {
std::string text = "Hello World C++ Programming";
auto words = split(text);
for (const auto& word : words) {
std::cout << word << std::endl;
}
return 0;
}
Пользовательский разделитель
Вот как разделить строку любым разделителем:
#include <iostream>
#include <vector>
#include <string>
std::vector<std::string> split(const std::string& str, char delimiter) {
std::vector<std::string> tokens;
size_t start = 0;
size_t end = str.find(delimiter);
while (end != std::string::npos) {
tokens.push_back(str.substr(start, end - start));
start = end + 1;
end = str.find(delimiter, start);
}
tokens.push_back(str.substr(start));
return tokens;
}
int main() {
std::string csv = "apple,banana,orange,grape";
auto fruits = split(csv, ',');
for (const auto& fruit : fruits) {
std::cout << fruit << std::endl;
}
return 0;
}
Сложное разделение несколькими разделителями
Обработка нескольких разделителей и пустых полей:
#include <iostream>
#include <vector>
#include <string>
#include <set>
class StringSplitter {
public:
static std::vector<std::string> split(const std::string& str,
const std::string& delimiters,
bool keepEmpty = false) {
std::vector<std::string> tokens;
std::string::size_type pos = 0;
std::string::size_type prev = 0;
while ((pos = str.find_first_of(delimiters, prev)) != std::string::npos) {
if (keepEmpty || pos > prev) {
tokens.push_back(str.substr(prev, pos - prev));
}
prev = pos + 1;
}
if (prev < str.length()) {
tokens.push_back(str.substr(prev));
} else if (keepEmpty && prev == str.length()) {
tokens.push_back("");
}
return tokens;
}
};
int main() {
std::string text = "one,two;;three;four,five";
auto tokens = StringSplitter::split(text, ",;", true);
for (size_t i = 0; i < tokens.size(); ++i) {
std::cout << "Token " << i << ": '" << tokens[i] << "'" << std::endl;
}
return 0;
}
Реальный сценарий: синтаксический анализатор CSV
Вот реальный синтаксический анализатор CSV, которым обрабатываются закавыченные поля:
#include <iostream>
#include <vector>
#include <string>
#include <stdexcept>
class CSVParser {
private:
static bool isQuoted(const std::string& field) {
return field.size() >= 2 &&
field.front() == '"' &&
field.back() == '"';
}
static std::string unquote(const std::string& field) {
if (!isQuoted(field)) return field;
return field.substr(1, field.size() - 2);
}
public:
static std::vector<std::string> parseCSVLine(const std::string& line) {
std::vector<std::string> fields;
std::string field;
bool inQuotes = false;
for (char c : line) {
if (c == '"') {
inQuotes = !inQuotes;
field += c;
} else if (c == ',' && !inQuotes) {
fields.push_back(unquote(field));
field.clear();
} else {
field += c;
}
}
fields.push_back(unquote(field)); // Добавляется последнее поле
return fields;
}
};
int main() {
std::string csvLine = "John,\"Doe,Jr\",\"123 Main St, Apt 4\",42";
auto fields = CSVParser::parseCSVLine(csvLine);
for (size_t i = 0; i < fields.size(); ++i) {
std::cout << "Field " << i << ": " << fields[i] << std::endl;
}
return 0;
}
Парсер аргументов командной строки
Так выполняются разделение и синтаксический анализ аргументов командной строки:
#include <iostream>
#include <vector>
#include <string>
#include <unordered_map>
class ArgumentParser {
private:
std::unordered_map<std::string, std::string> args;
public:
void parse(int argc, char* argv[]) {
for (int i = 1; i < argc; ++i) {
std::string arg = argv[i];
if (arg.substr(0, 2) == "--") {
size_t equals = arg.find('=');
if (equals != std::string::npos) {
std::string key = arg.substr(2, equals - 2);
std::string value = arg.substr(equals + 1);
args[key] = value;
} else {
args[arg.substr(2)] = "true";
}
}
}
}
std::string get(const std::string& key,
const std::string& defaultValue = "") const {
auto it = args.find(key);
return it != args.end() ? it->second : defaultValue;
}
};
int main(int argc, char* argv[]) {
ArgumentParser parser;
parser.parse(argc, argv);
// Применение: ./program --name=John --age=30 --verbose
std::string name = parser.get("name", "Unknown");
std::string age = parser.get("age", "0");
bool verbose = parser.get("verbose") == "true";
std::cout << "Name: " << name << std::endl;
std::cout << "Age: " << age << std::endl;
std::cout << "Verbose: " << (verbose ? "yes" : "no") << std::endl;
return 0;
}
Парсер URL-адресов
А так выполняются разделение и синтаксический анализ компонентов URL-адреса:
#include <iostream>
#include <string>
#include <unordered_map>
class URLParser {
private:
std::string protocol;
std::string host;
std::string path;
std::unordered_map<std::string, std::string> queryParams;
void parseQueryString(const std::string& query) {
size_t start = 0;
size_t end = query.find('&');
while (end != std::string::npos) {
parseParam(query.substr(start, end - start));
start = end + 1;
end = query.find('&', start);
}
parseParam(query.substr(start));
}
void parseParam(const std::string& param) {
size_t equals = param.find('=');
if (equals != std::string::npos) {
std::string key = param.substr(0, equals);
std::string value = param.substr(equals + 1);
queryParams[key] = value;
}
}
public:
void parse(const std::string& url) {
// Находится протокол
size_t protocolEnd = url.find("://");
if (protocolEnd != std::string::npos) {
protocol = url.substr(0, protocolEnd);
protocolEnd += 3; // Пропускается «"://"»
} else {
protocolEnd = 0;
}
// Находятся хост и путь
size_t pathStart = url.find('/', protocolEnd);
if (pathStart != std::string::npos) {
host = url.substr(protocolEnd, pathStart - protocolEnd);
// Находится строка запроса
size_t queryStart = url.find('?', pathStart);
if (queryStart != std::string::npos) {
path = url.substr(pathStart, queryStart - pathStart);
parseQueryString(url.substr(queryStart + 1));
} else {
path = url.substr(pathStart);
}
} else {
host = url.substr(protocolEnd);
path = "/";
}
}
void print() const {
std::cout << "Protocol: " << protocol << std::endl;
std::cout << "Host: " << host << std::endl;
std::cout << "Path: " << path << std::endl;
std::cout << "Query parameters:" << std::endl;
for (const auto& param : queryParams) {
std::cout << " " << param.first << " = " << param.second << std::endl;
}
}
};
int main() {
URLParser parser;
parser.parse("https://example.com/path?name=John&age=30");
parser.print();
return 0;
}
Заключение
Разделение строк реализуется на C++ по-разному в зависимости от потребностей:
- Для простого разделения пробелом используется istringstream.
- Для базовых задач применяется пользовательский разделитель.
- Для сложных случаев обрабатывается несколько разделителей.
- Для специфичных форматов — CSV, URL-адреса и т. д. — используются специализированные парсеры.
Обобщим ключевые моменты:
- Работая с большими строками, учитывайте последствиями для производительности.
- Обрабатывайте пограничные случаи: пустые поля, экранированные разделители.
- Выбирайте подход, исходя из формата ваших данных.
- Экспериментируйте с различными вариантами ввода.
- Для результатов используйте стандартные библиотечные контейнеры.
Читайте также:
- C++: полное руководство по обработке файлов с fstream
- C++: подробное руководство по размерам векторов
- C++: подробный разбор count_if
Читайте нас в Telegram, VK и Дзен
Перевод статьи ryan: Splitting Strings in C++: A Complete Guide





