Как я могу токенизировать строку в C++?
У Java есть удобный метод разбиения:
String str = "The quick brown fox";
String[] results = str.split(" ");
Есть ли простой способ сделать это в C++?
38 ответов
Ваш простой случай может быть легко построен с использованием std::string::find
метод. Однако взгляните на Boost.Tokenizer. Это великолепно. В Boost обычно есть очень крутые струнные инструменты.
Класс Boost tokenizer может сделать такие вещи довольно простыми:
#include <iostream>
#include <string>
#include <boost/foreach.hpp>
#include <boost/tokenizer.hpp>
using namespace std;
using namespace boost;
int main(int, char**)
{
string text = "token, test string";
char_separator<char> sep(", ");
tokenizer< char_separator<char> > tokens(text, sep);
BOOST_FOREACH (const string& t, tokens) {
cout << t << "." << endl;
}
}
Обновлено для C++11:
#include <iostream>
#include <string>
#include <boost/tokenizer.hpp>
using namespace std;
using namespace boost;
int main(int, char**)
{
string text = "token, test string";
char_separator<char> sep(", ");
tokenizer<char_separator<char>> tokens(text, sep);
for (const auto& t : tokens) {
cout << t << "." << endl;
}
}
Вот очень простой пример:
#include <vector>
#include <string>
using namespace std;
vector<string> split(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;
}
Еще один быстрый способ заключается в использовании getline
, Что-то вроде:
stringstream ss("bla bla");
string s;
while (getline(ss, s, ' ')) {
cout << s << endl;
}
Если вы хотите, вы можете сделать простой split()
метод, возвращающий vector<string>
, что действительно полезно.
Используйте стрток. На мой взгляд, нет необходимости создавать класс вокруг токенизации, если только strtok не предоставит вам то, что вам нужно. Возможно, нет, но за 15 с лишним лет написания различного кода синтаксического анализа на C и C++ я всегда использовал strtok. Вот пример
char myString[] = "The quick brown fox";
char *p = strtok(myString, " ");
while (p) {
printf ("Token: %s\n", p);
p = strtok(NULL, " ");
}
Несколько предостережений (которые могут не соответствовать вашим потребностям). В процессе "строка" "уничтожается", что означает, что символы EOS располагаются внутри точек разделителя. Для правильного использования может потребоваться сделать неконстантную версию строки. Вы также можете изменить список разделителей mid parse.
По моему мнению, приведенный выше код гораздо проще и проще в использовании, чем написание для него отдельного класса. Для меня это одна из тех функций, которые предоставляет язык, и делает это хорошо и чисто. Это просто решение на основе Си. Это уместно, это легко, и вам не нужно писать много лишнего кода:-)
Вы можете использовать потоки, итераторы и алгоритм копирования, чтобы сделать это довольно напрямую.
#include <string>
#include <vector>
#include <iostream>
#include <istream>
#include <ostream>
#include <iterator>
#include <sstream>
#include <algorithm>
int main()
{
std::string str = "The quick brown fox";
// construct a stream from the string
std::stringstream strstr(str);
// use stream iterators to copy the stream to the vector as whitespace separated strings
std::istream_iterator<std::string> it(strstr);
std::istream_iterator<std::string> end;
std::vector<std::string> results(it, end);
// send the vector to stdout.
std::ostream_iterator<std::string> oit(std::cout);
std::copy(results.begin(), results.end(), oit);
}
Решение с использованием regex_token_iterator
s:
#include <iostream>
#include <regex>
#include <string>
using namespace std;
int main()
{
string str("The quick brown fox");
regex reg("\\s+");
sregex_token_iterator iter(str.begin(), str.end(), reg, -1);
sregex_token_iterator end;
vector<string> vec(iter, end);
for (auto a : vec)
{
cout << a << endl;
}
}
Без обид, ребята, но для такой простой проблемы вы делаете вещи слишком сложными. Есть много причин, чтобы использовать Boost. Но для чего-то такого простого, это все равно что летать на санях с 20#.
void
split( vector<string> & theStringVector, /* Altered/returned value */
const string & theString,
const string & theDelimiter)
{
UASSERT( theDelimiter.size(), >, 0); // My own ASSERT macro.
size_t start = 0, end = 0;
while ( end != string::npos)
{
end = theString.find( theDelimiter, start);
// If at end, use length=maxLength. Else use length=end-start.
theStringVector.push_back( theString.substr( start,
(end == string::npos) ? string::npos : end - start));
// If at end, use start=maxSize. Else use start=end+delimiter.
start = ( ( end > (string::npos - theDelimiter.size()) )
? string::npos : end + theDelimiter.size());
}
}
Например (для Дуга)
#define SHOW(I,X) cout << "[" << (I) << "]\t " # X " = \"" << (X) << "\"" << endl
int
main()
{
vector<string> v;
split( v, "A:PEP:909:Inventory Item", ":" );
for (unsigned int i = 0; i < v.size(); i++)
SHOW( i, v[i] );
}
И да, мы могли бы использовать метод split(), возвращающий новый вектор, а не передавая его. Это тривиально для переноса и перегрузки. Но в зависимости от того, что я делаю, я часто нахожу лучше повторно использовать уже существующие объекты, а не всегда создавать новые. (Пока я не забуду опустошить вектор между ними!)
Ссылка: http://en.cppreference.com/w/cpp/string/basic_string.
(Первоначально я писал ответ на вопрос Дуга: модификация и извлечение строк C++ на основе разделителей (закрыто). Но поскольку Мартин Йорк закрыл этот вопрос с помощью указателя здесь... я просто обобщу свой код.)
Boost имеет сильную функцию разбиения: boost::gorith:: split.
Пример программы:
#include <vector>
#include <boost/algorithm/string.hpp>
int main() {
auto s = "a,b, c ,,e,f,";
std::vector<std::string> fields;
boost::split(fields, s, boost::is_any_of(","));
for (const auto& field : fields)
std::cout << "\"" << field << "\"\n";
return 0;
}
Выход:
"a"
"b"
" c "
""
"e"
"f"
""
Это простое STL-решение (~5 строк!) С использованием std::find
а также std::find_first_not_of
он обрабатывает повторения разделителя (например, пробелы или точки), а также начальные и конечные разделители:
#include <string>
#include <vector>
void tokenize(std::string str, std::vector<string> &token_v){
size_t start = str.find_first_not_of(DELIMITER), end=start;
while (start != std::string::npos){
// Find next occurence of delimiter
end = str.find(DELIMITER, start);
// Push back the token found into vector
token_v.push_back(str.substr(start, end-start));
// Skip all occurences of the delimiter to find new start
start = str.find_first_not_of(DELIMITER, end);
}
}
Попробуйте вживую!
Я знаю, что вы спрашивали о решении C++, но вы можете посчитать это полезным:
Qt
#include <QString>
...
QString str = "The quick brown fox";
QStringList results = str.split(" ");
Преимущество по сравнению с Boost в этом примере заключается в том, что это прямое сопоставление кода вашего сообщения.
Смотрите больше в документации Qt
Вот пример класса токенизатора, который может делать то, что вы хотите
//Header file
class Tokenizer
{
public:
static const std::string DELIMITERS;
Tokenizer(const std::string& str);
Tokenizer(const std::string& str, const std::string& delimiters);
bool NextToken();
bool NextToken(const std::string& delimiters);
const std::string GetToken() const;
void Reset();
protected:
size_t m_offset;
const std::string m_string;
std::string m_token;
std::string m_delimiters;
};
//CPP file
const std::string Tokenizer::DELIMITERS(" \t\n\r");
Tokenizer::Tokenizer(const std::string& s) :
m_string(s),
m_offset(0),
m_delimiters(DELIMITERS) {}
Tokenizer::Tokenizer(const std::string& s, const std::string& delimiters) :
m_string(s),
m_offset(0),
m_delimiters(delimiters) {}
bool Tokenizer::NextToken()
{
return NextToken(m_delimiters);
}
bool Tokenizer::NextToken(const std::string& delimiters)
{
size_t i = m_string.find_first_not_of(delimiters, m_offset);
if (std::string::npos == i)
{
m_offset = m_string.length();
return false;
}
size_t j = m_string.find_first_of(delimiters, i);
if (std::string::npos == j)
{
m_token = m_string.substr(i);
m_offset = m_string.length();
return true;
}
m_token = m_string.substr(i, j - i);
m_offset = j;
return true;
}
Пример:
std::vector <std::string> v;
Tokenizer s("split this string", " ");
while (s.NextToken())
{
v.push_back(s.GetToken());
}
pystring - это небольшая библиотека, которая реализует множество строковых функций Python, включая метод split:
#include <string>
#include <vector>
#include "pystring.h"
std::vector<std::string> chunks;
pystring::split("this string", chunks);
// also can specify a separator
pystring::split("this-string", chunks, "-");
Если вы используете диапазоны C++ - полную библиотеку диапазонов v3, а не ограниченную функциональность, принятую в C++20, - вы можете сделать это следующим образом:
auto results = str | ranges::views::tokenize(" ",1);
... и это вычисляется лениво, то есть O(1) время и пространство. В качестве альтернативы вы можете установить вектор в этот диапазон:
auto results = str | ranges::views::tokenize(" ",1) | to<std::vector>();
это займет O(m) пространства и O(n) времени, если str
содержит n символов, составляющих m слов.
См. Также пример токенизации библиотеки здесь.
Я отправил этот ответ на аналогичный вопрос.
Не изобретай велосипед. Я использовал несколько библиотек, и самая быстрая и гибкая из всех, с которыми я сталкивался, это: C++ String Toolkit Library.
Вот пример того, как его использовать, который я разместил где-то в стеке потока.
#include <iostream>
#include <vector>
#include <string>
#include <strtk.hpp>
const char *whitespace = " \t\r\n\f";
const char *whitespace_and_punctuation = " \t\r\n\f;,=";
int main()
{
{ // normal parsing of a string into a vector of strings
std::string s("Somewhere down the road");
std::vector<std::string> result;
if( strtk::parse( s, whitespace, result ) )
{
for(size_t i = 0; i < result.size(); ++i )
std::cout << result[i] << std::endl;
}
}
{ // parsing a string into a vector of floats with other separators
// besides spaces
std::string t("3.0, 3.14; 4.0");
std::vector<float> values;
if( strtk::parse( s, whitespace_and_punctuation, values ) )
{
for(size_t i = 0; i < values.size(); ++i )
std::cout << values[i] << std::endl;
}
}
{ // parsing a string into specific variables
std::string u("angle = 45; radius = 9.9");
std::string w1, w2;
float v1, v2;
if( strtk::parse( s, whitespace_and_punctuation, w1, v1, w2, v2) )
{
std::cout << "word " << w1 << ", value " << v1 << std::endl;
std::cout << "word " << w2 << ", value " << v2 << std::endl;
}
}
return 0;
}
В ответе Адама Пирса приведен токенайзер с ручным вращением, принимающий const char*
, Это немного сложнее сделать с итераторами, потому что увеличение string
конец итератора не определен. Это сказал, учитывая string str{ "The quick brown fox" }
мы, безусловно, можем сделать это:
auto start = find(cbegin(str), cend(str), ' ');
vector<string> tokens{ string(cbegin(str), start) };
while (start != cend(str)) {
const auto finish = find(++start, cend(str), ' ');
tokens.push_back(string(start, finish));
start = finish;
}
Если вы хотите абстрагироваться от сложности с помощью стандартной функциональности, как предлагает On Freund strtok
это простой вариант:
vector<string> tokens;
for (auto i = strtok(data(str), " "); i != nullptr; i = strtok(nullptr, " ")) tokens.push_back(i);
Если у вас нет доступа к C++17, вам нужно заменить data(str)
как в этом примере: http://ideone.com/8kAGoa
Хотя это не продемонстрировано в примере, strtok
не нужно использовать один и тот же разделитель для каждого токена. Наряду с этим преимуществом есть несколько недостатков:
strtok
не может использоваться на несколькихstrings
в то же время: либоnullptr
необходимо передать, чтобы продолжить токенизацию текущегоstring
или новыйchar*
tokenize должен быть передан (однако есть некоторые нестандартные реализации, которые поддерживают это, такие как:strtok_s
)- По той же причине
strtok
нельзя использовать в нескольких потоках одновременно (однако это может быть определено реализацией, например: реализация Visual Studio является поточно-ориентированной) - призвание
strtok
модифицируетstring
он работает, поэтому его нельзя использовать наconst string
s,const char*
s, или буквенные строки, чтобы маркировать любой из них сstrtok
или работать наstring
чье содержимое должно быть сохранено,str
должно быть скопировано, тогда копия может быть оперирована
Оба предыдущих метода не могут генерировать токены vector
на месте, то есть без абстрагирования их во вспомогательную функцию, которую они не могут инициализировать const vector<string> tokens
, Эта функциональность и возможность принимать любой разделитель пробелов может быть использован с помощью istream_iterator
, Например, учитывая: const string str{ "The quick \tbrown \nfox" }
мы можем это сделать:
istringstream is{ str };
const vector<string> tokens{ istream_iterator<string>(is), istream_iterator<string>() };
Требуемая конструкция istringstream
этот вариант имеет гораздо большую стоимость, чем предыдущие 2 варианта, однако эта стоимость обычно скрывается за счет string
распределение.
Если ни один из перечисленных выше вариантов не является достаточно гибким для ваших потребностей в токенизации, наиболее гибкий вариант - это использование regex_token_iterator
Конечно, такая гибкость сопряжена с большими затратами, но опять же, скорее всего, это скрыто в string
стоимость размещения. Скажем, например, мы хотим токенизировать на основе неэкранированных запятых, также используя пробел, с учетом следующего ввода: const string str{ "The ,qu\\,ick ,\tbrown, fox" }
мы можем это сделать:
const regex re{ "\\s*((?:[^\\\\,]|\\\\.)*?)\\s*(?:,|$)" };
const vector<string> tokens{ sregex_token_iterator(cbegin(str), cend(str), re, 1), sregex_token_iterator() };
Проверьте этот пример. Это может помочь вам..
#include <iostream>
#include <sstream>
using namespace std;
int main ()
{
string tmps;
istringstream is ("the dellimiter is the space");
while (is.good ()) {
is >> tmps;
cout << tmps << "\n";
}
return 0;
}
MFC/ATL имеет очень хороший токенизатор. Из MSDN:
CAtlString str( "%First Second#Third" );
CAtlString resToken;
int curPos= 0;
resToken= str.Tokenize("% #",curPos);
while (resToken != "")
{
printf("Resulting token: %s\n", resToken);
resToken= str.Tokenize("% #",curPos);
};
Output
Resulting Token: First
Resulting Token: Second
Resulting Token: Third
Вы можете просто использовать библиотеку регулярных выражений и решить ее с помощью регулярных выражений.
Используйте выражение (\w+) и переменную в \1 (или $1 в зависимости от реализации библиотеки регулярных выражений).
Я думал, что это было то, что >>
Оператор на строковых потоках был для:
string word; sin >> word;
Если вы хотите использовать C, вы можете использовать функцию strtok. Вы должны обратить внимание на проблемы многопоточности при его использовании.
Здесь много слишком сложных предложений. Попробуйте это простое решение std::string:
using namespace std;
string someText = ...
string::size_type tokenOff = 0, sepOff = tokenOff;
while (sepOff != string::npos)
{
sepOff = someText.find(' ', sepOff);
string::size_type tokenLen = (sepOff == string::npos) ? sepOff : sepOff++ - tokenOff;
string token = someText.substr(tokenOff, tokenLen);
if (!token.empty())
/* do something with token */;
tokenOff = sepOff;
}
Для простых вещей я просто использую следующее:
unsigned TokenizeString(const std::string& i_source,
const std::string& i_seperators,
bool i_discard_empty_tokens,
std::vector<std::string>& o_tokens)
{
unsigned prev_pos = 0;
unsigned pos = 0;
unsigned number_of_tokens = 0;
o_tokens.clear();
pos = i_source.find_first_of(i_seperators, pos);
while (pos != std::string::npos)
{
std::string token = i_source.substr(prev_pos, pos - prev_pos);
if (!i_discard_empty_tokens || token != "")
{
o_tokens.push_back(i_source.substr(prev_pos, pos - prev_pos));
number_of_tokens++;
}
pos++;
prev_pos = pos;
pos = i_source.find_first_of(i_seperators, pos);
}
if (prev_pos < i_source.length())
{
o_tokens.push_back(i_source.substr(prev_pos));
number_of_tokens++;
}
return number_of_tokens;
}
Трусливый отказ от ответственности: я пишу программное обеспечение для обработки данных в режиме реального времени, в котором данные поступают через двоичные файлы, сокеты или некоторые вызовы API (карты ввода-вывода, камеры). Я никогда не использую эту функцию для чего-то более сложного или критичного ко времени, чем чтение внешних файлов конфигурации при запуске.
Я знаю, что на этот вопрос уже дан ответ, но я хочу внести свой вклад. Может быть, мое решение немного простое, но вот что я придумал:
vector<string> get_words(string const& text)
{
vector<string> result;
string tmp = text;
size_t first_pos = 0;
size_t second_pos = tmp.find(" ");;
while (second_pos != string::npos)
{
if (first_pos != second_pos)
{
string word = tmp.substr(first_pos, second_pos - first_pos);
result.push_back(word);
}
tmp = tmp.substr(second_pos + 1);
second_pos = tmp.find(" ");
}
return result;
}
Пожалуйста, прокомментируйте, есть ли лучший подход к чему-либо в моем коде или что-то не так.
Мне кажется странным, что со всеми нами, любителями скорости, здесь, на SO, никто не представил версию, которая использует сгенерированную во время компиляции справочную таблицу для разделителя (пример реализации ниже). Использование справочной таблицы и итераторов должно превзойти эффективность std:: regex, если вам не нужно разбивать регулярное выражение, просто используйте его, его стандарт на C++11 и супер гибкий.
Некоторые уже предложили регулярное выражение, но для noobs здесь приведен упакованный пример, который должен делать именно то, что ожидает OP:
std::vector<std::string> split(std::string::const_iterator it, std::string::const_iterator end, std::regex e = std::regex{"\\w+"}){
std::smatch m{};
std::vector<std::string> ret{};
while (std::regex_search (it,end,m,e)) {
ret.emplace_back(m.str());
std::advance(it, m.position() + m.length()); //next start position = match position + match length
}
return ret;
}
std::vector<std::string> split(const std::string &s, std::regex e = std::regex{"\\w+"}){ //comfort version calls flexible version
return split(s.cbegin(), s.cend(), std::move(e));
}
int main ()
{
std::string str {"Some people, excluding those present, have been compile time constants - since puberty."};
auto v = split(str);
for(const auto&s:v){
std::cout << s << std::endl;
}
std::cout << "crazy version:" << std::endl;
v = split(str, std::regex{"[^e]+"}); //using e as delim shows flexibility
for(const auto&s:v){
std::cout << s << std::endl;
}
return 0;
}
Если нам нужно быть быстрее и принять ограничение, что все символы должны быть 8 битами, мы можем создать таблицу поиска во время компиляции, используя метапрограммирование:
template<bool...> struct BoolSequence{}; //just here to hold bools
template<char...> struct CharSequence{}; //just here to hold chars
template<typename T, char C> struct Contains; //generic
template<char First, char... Cs, char Match> //not first specialization
struct Contains<CharSequence<First, Cs...>,Match> :
Contains<CharSequence<Cs...>, Match>{}; //strip first and increase index
template<char First, char... Cs> //is first specialization
struct Contains<CharSequence<First, Cs...>,First>: std::true_type {};
template<char Match> //not found specialization
struct Contains<CharSequence<>,Match>: std::false_type{};
template<int I, typename T, typename U>
struct MakeSequence; //generic
template<int I, bool... Bs, typename U>
struct MakeSequence<I,BoolSequence<Bs...>, U>: //not last
MakeSequence<I-1, BoolSequence<Contains<U,I-1>::value,Bs...>, U>{};
template<bool... Bs, typename U>
struct MakeSequence<0,BoolSequence<Bs...>,U>{ //last
using Type = BoolSequence<Bs...>;
};
template<typename T> struct BoolASCIITable;
template<bool... Bs> struct BoolASCIITable<BoolSequence<Bs...>>{
/* could be made constexpr but not yet supported by MSVC */
static bool isDelim(const char c){
static const bool table[256] = {Bs...};
return table[static_cast<int>(c)];
}
};
using Delims = CharSequence<'.',',',' ',':','\n'>; //list your custom delimiters here
using Table = BoolASCIITable<typename MakeSequence<256,BoolSequence<>,Delims>::Type>;
С этим на месте делает getNextToken
Функция проста:
template<typename T_It>
std::pair<T_It,T_It> getNextToken(T_It begin,T_It end){
begin = std::find_if(begin,end,std::not1(Table{})); //find first non delim or end
auto second = std::find_if(begin,end,Table{}); //find first delim or end
return std::make_pair(begin,second);
}
Используя это также легко:
int main() {
std::string s{"Some people, excluding those present, have been compile time constants - since puberty."};
auto it = std::begin(s);
auto end = std::end(s);
while(it != std::end(s)){
auto token = getNextToken(it,end);
std::cout << std::string(token.first,token.second) << std::endl;
it = token.second;
}
return 0;
}
Вот живой пример: http://ideone.com/GKtkLQ
Вот подход, который позволяет вам контролировать, включены ли пустые токены (например, strsep) или исключены (например, strtok).
#include <string.h> // for strchr and strlen
/*
* want_empty_tokens==true : include empty tokens, like strsep()
* want_empty_tokens==false : exclude empty tokens, like strtok()
*/
std::vector<std::string> tokenize(const char* src,
char delim,
bool want_empty_tokens)
{
std::vector<std::string> tokens;
if (src and *src != '\0') // defensive
while( true ) {
const char* d = strchr(src, delim);
size_t len = (d)? d-src : strlen(src);
if (len or want_empty_tokens)
tokens.push_back( std::string(src, len) ); // capture token
if (d) src += len+1; else break;
}
return tokens;
}
Я написал упрощенную версию (и, возможно, немного эффективную) /questions/6839420/kak-ya-mogu-tokenizirovat-stroku-v-c/6839449#6839449 для собственного использования. Надеюсь, это поможет.
void StrTokenizer(string& source, const char* delimiter, vector<string>& Tokens)
{
size_t new_index = 0;
size_t old_index = 0;
while (new_index != std::string::npos)
{
new_index = source.find(delimiter, old_index);
Tokens.emplace_back(source.substr(old_index, new_index-old_index));
if (new_index != std::string::npos)
old_index = ++new_index;
}
}
Нет прямого способа сделать это. Обратитесь к этому исходному коду проекта кода, чтобы узнать, как создать класс для этого.
Вы можете воспользоваться boost::make_find_iterator. Что-то похожее на это:
template<typename CH>
inline vector< basic_string<CH> > tokenize(
const basic_string<CH> &Input,
const basic_string<CH> &Delimiter,
bool remove_empty_token
) {
typedef typename basic_string<CH>::const_iterator string_iterator_t;
typedef boost::find_iterator< string_iterator_t > string_find_iterator_t;
vector< basic_string<CH> > Result;
string_iterator_t it = Input.begin();
string_iterator_t it_end = Input.end();
for(string_find_iterator_t i = boost::make_find_iterator(Input, boost::first_finder(Delimiter, boost::is_equal()));
i != string_find_iterator_t();
++i) {
if(remove_empty_token){
if(it != i->begin())
Result.push_back(basic_string<CH>(it,i->begin()));
}
else
Result.push_back(basic_string<CH>(it,i->begin()));
it = i->end();
}
if(it != it_end)
Result.push_back(basic_string<CH>(it,it_end));
return Result;
}
Я только что прочитал все ответы и не могу найти решение со следующими предварительными условиями:
- нет динамического выделения памяти
- без использования наддува
- без использования регулярных выражений
- только стандарт С++ 17
Итак, вот мое решение
#include <iomanip>
#include <iostream>
#include <iterator>
#include <string_view>
#include <utility>
struct split_by_spaces
{
std::string_view text;
static constexpr char delim = ' ';
struct iterator
{
const std::string_view& text;
std::size_t cur_pos;
std::size_t end_pos;
std::string_view operator*() const
{
return { &text[cur_pos], end_pos - cur_pos };
}
bool operator==(const iterator& other) const
{
return cur_pos == other.cur_pos && end_pos == other.end_pos;
}
bool operator!=(const iterator& other) const
{
return !(*this == other);
}
iterator& operator++()
{
cur_pos = text.find_first_not_of(delim, end_pos);
if (cur_pos == std::string_view::npos)
{
cur_pos = text.size();
end_pos = cur_pos;
return *this;
}
end_pos = text.find(delim, cur_pos);
if (cur_pos == std::string_view::npos)
{
end_pos = text.size();
}
return *this;
}
};
[[nodiscard]] iterator begin() const
{
auto start = text.find_first_not_of(delim);
if (start == std::string_view::npos)
{
return iterator{ text, text.size(), text.size() };
}
auto end_word = text.find(delim, start);
if (end_word == std::string_view::npos)
{
end_word = text.size();
}
return iterator{ text, start, end_word };
}
[[nodiscard]] iterator end() const
{
return iterator{ text, text.size(), text.size() };
}
};
int main(int argc, char** argv)
{
using namespace std::literals;
auto str = " there should be no memory allocation during parsing"
" into words this line and you should'n create any"
" contaner for intermediate words "sv;
auto comma = "";
for (std::string_view word : split_by_spaces{ str })
{
std::cout << std::exchange(comma, ",") << std::quoted(word);
}
auto only_spaces = " "sv;
for (std::string_view word : split_by_spaces{ only_spaces })
{
std::cout << "you will not see this line in output" << std::endl;
}
}