Вот моя запись:
template <typename Container, typename InputIter, typename ForwardIter>
Container
split(InputIter first, InputIter last,
ForwardIter s_first, ForwardIter s_last)
{
Container output;
while (true) {
auto pos = std::find_first_of(first, last, s_first, s_last);
output.emplace_back(first, pos);
if (pos == last) {
break;
}
first = ++pos;
}
return output;
}
template <typename Output = std::vector<std::string>,
typename Input = std::string,
typename Delims = std::string>
Output
split(const Input& input, const Delims& delims = " ")
{
using std::cbegin;
using std::cend;
return split<Output>(cbegin(input), cend(input),
cbegin(delims), cend(delims));
}
auto vec = split("Mary had a little lamb");
Первое определение - обобщенная функция в стиле STL, принимающая две пары итераторов. Вторая - это удобная функция, которая избавляет вас от необходимости делать все begin()
s и end()
s самостоятельно. Вы также можете указать тип выходного контейнера в качестве параметра шаблона, если вы хотите использовать, например, list
.
Что делает его элегантным (IMO), так это то, что в отличие от большинства других ответов, он не ограничивается строками, но будет работать с любым STL-совместимым контейнером. Без каких-либо изменений в коде выше, вы можете сказать:
using vec_of_vecs_t = std::vector<std::vector<int>>;
std::vector<int> v{1, 2, 0, 3, 4, 5, 0, 7, 8, 0, 9};
auto r = split<vec_of_vecs_t>(v, std::initializer_list<int>{0, 2});
, который будет разбивать вектор v
на отдельные векторы каждый раз, когда встречается 0
или 2
.
(Существует также дополнительный бонус, заключающийся в том, что со строками эта реализация быстрее, чем обе версии на основе strtok()
- и getline()
, по крайней мере, в моей системе.)