Разделение строки на токены с несколькими возможными разделителями с использованием `std::ranges`C++

Программы на C++. Форум разработчиков
Ответить
Anonymous
 Разделение строки на токены с несколькими возможными разделителями с использованием `std::ranges`

Сообщение Anonymous »

Моя цель — разделить std::string на токены, разделенные списком возможных разделителей/разделителей.
Например, std::string line{"\tSplit \t\t эта последовательность\t токенов "}; необходимо разделить на Split,

Код: Выделить всё

this,sequence,of,tokens
.
Это хорошо известная проблема, и можно найти множество реализаций:
  • с помощью std::isstream
  • с помощью std::regex
  • с помощью std::strtok
  • с помощью find или find_first_of
Насколько мне известно, первые две требуют выделения промежуточных подстрок, а регулярное выражение работает особенно медленно.

Код: Выделить всё

std::strtok
не является потокобезопасным, так как поддерживает внутреннее состояние в виде статических переменных.
Использование find довольно простое и быстрое (см. тесты ниже), но код довольно многословен (я знаю, это субъективно).
Я хочу протестировать более современные, более читабельные и, надеюсь, более эффективные способы использования std::ranges.
Естественный путь кажется таким: через std::view::split, но он поддерживает только один разделитель. В этом случае быстрый поиск в Интернете возвращает такую ​​реализацию:

Код: Выделить всё

auto tokenize_with_ranges(std::string const& str) {
auto tokens = str | std::views::split(' ') |
std::views::filter([](auto&& rng) { return !rng.empty(); }) |
std::views::transform([](auto&& rng) {
return std::string_view(rng.begin(), rng.end());
});

std::vector token_vector;
for (auto const token : tokens) {
token_vector.push_back(token);
}
return token_vector;
}
LIVE
Но чтобы добавить второй разделитель, я разработал его сам и не смог добиться лучшего, чем:

Код: Выделить всё

auto tokenize_with_ranges(std::string const& str) {
auto tokens =
str | std::views::split('\t') |
std::views::transform([](auto&& tokens_splitted_on_tab) {
return tokens_splitted_on_tab | std::views::split(' ') |
std::views::filter([](auto&& rng) { return !rng.empty(); }) |
std::views::transform([](auto&& rng) {
return std::string_view(rng.begin(), rng.end());
});
}) |
std::views::join;
std::vector token_vector;
for (auto const token : tokens) {
token_vector.push_back(token);
}
return token_vector;
}
LIVE
Я думаю, что это нелегко читать и невозможно масштабировать, если я хочу добавить другие разделители.
Есть ли способ преодолеть ограничение std::view::split и использовать диапазоны для разделения строки с разными разделителями?

Дополнительный контекст: сравнение
Я быстро установил тест (надеюсь, что не допустил ошибок), получив следующий рейтинг (с ускорением):

Код: Выделить всё

std::regex
(самый медленный, x1) >> std::isstream (x3.6) >> std::ranges (x8.5) > std::strtok (x11) > find (самый быстрый, x13)

Подробнее здесь: https://stackoverflow.com/questions/798 ... ing-stdran
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «C++»