Токенизатор в string надо бы

crea7or
crea7or

11 строк кода всегда с собой таскать что ли? Ну для полноты картины можно специфицировать для использования с разными контейнерами и т.д. (можно заняться, если надо). Ну ведь часто надо же и везде приходится либо так либо дремучий и деструктивный strtok использовать. В бусте есть токенизатор, но он сложный какой-то. Реализация влоб для демонстрации:

vector<string> splitLine( const char *str, char c )
{
	vector<string> result;
	do
	{
		const char *begin = str;
		while ( *str != c && *str )
		{
			str++;
		}
		result.push_back( string( begin, str ));
	} while ( 0 != *str++ );
	return result;
}
12
рейтинг
20 комментариев
yndx-antoshkka
Идея хорошая. Надо поменять имплементацию на более обобщённую, чтобы:
* на вход принимала итераторы, а не с контейнер
* выдавала результат в OutputIterator а не в std::vector<std::string>
* не аллоцировала пямять (чтобы можно было подставить в него на выход итератор, принимающий string_view)
* могла сплитить и по одному символу, и по нескольким, и используя функтор

Но тогда у вас получится сто-то похожее на boost::split ...
yndx-antoshkka
crea7or
yndx-antoshkka, я вот задумался над реализацией в виде for_each(..) - только each_tag(...) например. Вариантов-то много, главное чтобы хоть какой-то был в стандарте. Ну хоть буст::сплит, хотя он и странно выглядит по входным параметрам.
crea7or
h4tred
А если делить по строке, а не по символу? Тогда нужно притянуть и boost::find_all, что бы на базе его строить.
h4tred
crea7or
h4tred, было бы логично делить на Т в контейнере с Т. Всё остальное уже слишком для стандарта, как мне кажется.
crea7or
crea7or
yndx-antoshkka, ну вот как-то так. Что-то не виду преимуществ Output итератора (тут две версии, с ним и без него): github.com/crea7or/stringslice/blob/master/slicestd/slicestd.cpp
crea7or
Сергей Садовников
Вообще, в качестве альтернативы, можно использовать regexp'ы, которые уже есть в стандарте.
Сергей Садовников
crea7or
Сергей Садовников, это же насколько оно будет медленнее?
crea7or
Сергей Садовников
crea7or, ну, это можно проверить. Не факт, что сильно медленнее.
Сергей Садовников
Сергей Садовников
Более того, в std::regex уже есть такая конструкция:
en.cppreference.com/w/cpp/regex/regex_token_iterator
Сергей Садовников
crea7or
Сергей Садовников, я потратил 10 минут - 20+ раз разница с тем что вверху. Регэкспы это в принципе штука не быстрая.
crea7or
Сергей Садовников
Да. Согласен. По сравнению с достаточно простым алгоритмом "в лоб" разница действительно в 20 раз. А вот с бустовым алгоритмом, который более хитрый и предлагает больше количество классификаторов для сплиттера - разница уже меньше - всего в два раза. Регэкспы - штука, конечно, не быстрая. Но достаточно быстрая для задач обработки строк, отличающихся по сложности от простого разделения по символам.

wandbox.org/permlink/YsLohgLdnlv1TCTQ
Сергей Садовников
crea7or
Сергей Садовников, ок, я тогда попробую по своему в духе стандарта сделать и дальше посмотрим.
crea7or
crea7or
Сергей Садовников, сделал я any_slice(в том числе) - всё равно разница в 20 раз с regexp'ом. Понятно, что для сложных штук придумывать ничего в стандарте не надо. Или своё или брать регэксп, но это-то задача не такого порядка: wandbox.org/permlink/3hm9rXLZYyUXbX8b
crea7or
zamazan4ik@tut.by
Boost.Algorithm (а точнее Boost.StringAlgo) умеет прекрасно делать такое. Если дождёмся unified call syntax, то будет вообще лепота.
zamazan4ik@tut.by
crea7or
Два предложения на эту тему уже есть. Первое, похоже, не прошло. Второе похоже на дельную вещь, но как мне кажется сильно сложное.
open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3593.html
open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0540r0.html
crea7or
maxim.perenesenko
А как вам, например, такая реализация:

template<typename _Ty, typename _InputIt, typename _OutputIt, typename _DelimsIt>
_OutputIt split(_InputIt first, _InputIt last, _DelimsIt d_first, _DelimsIt d_last, _OutputIt out)
{
_InputIt start = first;
while( first != last )
{
if( std::find(d_first, d_last, *first) != d_last )
{
*out++ = _Ty(start, first);
start = first + 1;
}
++first;
}
if( start != last )
*out++ = _Ty(start, first);
return out;
}

Использовать, например можно так:

std::string s = "Some string, to tokenize.last";
std::deque<char> delims{ ' ', ',', '.' };
stdx::split<std::string>(std::begin(s), std::end(s), std::begin(delims), std::end(delims),
std::ostream_iterator<std::string>(std::cout, "\n"));

Вставил ее в тест по времени от crea7or:
wandbox.org/permlink/psULR142adeDiuMp
maxim.perenesenko
yndx-antoshkka
maxim.perenesenko, огонь! Предлагаю пойти длинным путём и сначала добавить в Boost в виде pull request: github.com/boostorg/algorithm (только добавляйте в общие алгоритмы, а не в string)

Похоже что можно убрать первый шаблонные параметр и просто писать *out++ = {start, first};. Возможно что умельцы из Boost подметят ещё какие-то вещи.
yndx-antoshkka
smertigdon
maxim.perenesenko, часто используется split по одному разделителю, будет неплохо также сделать реализацию с таким функционалом, иначе костыльно получится
smertigdon
maxim.perenesenko
smertigdon, тоже думал об этом. Спасибо.
maxim.perenesenko
maxim.perenesenko
yndx-antoshkka, Ни разу этого не делал, но попробую. О результатах отпишусь тут.
maxim.perenesenko
Другие идеи
Группа создана, чтобы собирать предложения к стандарту C++, организовывать их внутренние обсуждения, помогать готовить их для отправки в комитет и защищать на общих собраниях в рабочей группе по С++ Международной организации по стандартизации (ISO).