Como faço para tokenizar uma string em C ++?

Java tem um método de divisão conveniente:

String str = "The quick brown fox"; String[] results = str.split(" "); 

Existe uma maneira fácil de fazer isso em C ++?

Seu caso simples pode ser facilmente construído usando o método std::string::find . No entanto, dê uma olhada no Boost.Tokenizer . É ótimo. O Boost geralmente possui algumas ferramentas de string muito legais.

A class do tokenizer Boost pode tornar esse tipo de coisa bastante simples:

 #include  #include  #include  #include  using namespace std; using namespace boost; int main(int, char**) { string text = "token, test string"; char_separator sep(", "); tokenizer< char_separator > tokens(text, sep); BOOST_FOREACH (const string& t, tokens) { cout < < t << "." << endl; } } 

Atualizado para o C ++ 11:

 #include  #include  #include  using namespace std; using namespace boost; int main(int, char**) { string text = "token, test string"; char_separator sep(", "); tokenizer> tokens(text, sep); for (const auto& t : tokens) { cout < < t << "." << endl; } } 

Aqui está um simples real:

 #include  #include  using namespace std; vector split(const char *str, char c = ' ') { vector result; do { const char *begin = str; while(*str != c && *str) str++; result.push_back(string(begin, str)); } while (0 != *str++); return result; } 

Use strtok. Na minha opinião, não há necessidade de criar uma class em torno de tokenizing a menos que strtok não forneça o que você precisa. Pode não ser, mas em mais de 15 anos escrevendo vários códigos de análise em C e C ++, eu sempre usei strtok. Aqui está um exemplo

 char myString[] = "The quick brown fox"; char *p = strtok(myString, " "); while (p) { printf ("Token: %s\n", p); p = strtok(NULL, " "); } 

Algumas advertências (que podem não atender às suas necessidades). A string é “destruída” no processo, o que significa que os caracteres EOS são colocados em linha nos pontos delimitadores. O uso correto pode exigir que você faça uma versão não const da string. Você também pode alterar a lista de delimitadores no meio da análise.

Na minha opinião, o código acima é muito mais simples e fácil de usar do que escrever uma class separada para ele. Para mim, esta é uma daquelas funções que a linguagem fornece e faz isso bem e de forma limpa. É simplesmente uma solução “baseada em C”. É apropriado, é fácil e você não precisa escrever muito código extra 🙂

Outra maneira rápida é usar o getline . Algo como:

 stringstream ss("bla bla"); string s; while (getline(ss, s, ' ')) { cout < < s << endl; } 

Se você quiser, você pode fazer um método simples split() retornando um vector , o que é realmente útil.

Você pode usar streams, iteradores e o algoritmo de cópia para fazer isso de maneira razoavelmente direta.

 #include  #include  #include  #include  #include  #include  #include  #include  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 it(strstr); std::istream_iterator end; std::vector results(it, end); // send the vector to stdout. std::ostream_iterator oit(std::cout); std::copy(results.begin(), results.end(), oit); } 

Sem ofensas, mas para um problema tão simples, você está complicando muito as coisas. Existem muitas razões para usar o Boost . Mas para algo tão simples, é como acertar uma mosca com um trenó 20 #.

 void split( vector & 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()); } } 

Por exemplo (para o caso de Doug),

 #define SHOW(I,X) cout < < "[" << (I) << "]\t " # X " = \"" << (X) << "\"" << endl int main() { vector v; split( v, "A:PEP:909:Inventory Item", ":" ); for (unsigned int i = 0; i < v.size(); i++) SHOW( i, v[i] ); } 

E sim, poderíamos dividir () retornar um novo vetor ao invés de passar um. É trivial para embrulhar e sobrecarregar. Mas, dependendo do que estou fazendo, muitas vezes acho melhor reutilizar objects pré-existentes, em vez de sempre criar novos. (Contanto que eu não esqueça de esvaziar o vetor entre os dois!)

Referência: http://www.cplusplus.com/reference/string/string/ .

(Eu estava originalmente escrevendo uma resposta à pergunta de Doug: Modificação e extração de strings C ++ com base em separadores (fechados) . Mas como Martin York fechou essa questão com um ponteiro aqui ... vou generalizar meu código.)

Boost tem uma function split forte: boost :: algorithm :: split .

Programa de amostra:

 #include  #include  int main() { auto s = "a,b, c ,,e,f,"; std::vector fields; boost::split(fields, s, boost::is_any_of(",")); for (const auto& field : fields) std::cout < < "\"" << field << "\"\n"; return 0; } 

Saída:

 "a" "b" " c " "" "e" "f" "" 

Uma solução usando regex_token_iterator s:

 #include  #include  #include  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 vec(iter, end); for (auto a : vec) { cout < < a << endl; } } 

Eu sei que você pediu uma solução C ++, mas você pode considerar isso útil:

Qt

 #include  ... QString str = "The quick brown fox"; QStringList results = str.split(" "); 

A vantagem sobre o Boost, neste exemplo, é que ele é um mapeamento direto de um para um para o código do seu post.

Veja mais na documentação do Qt

Aqui está uma class de tokenizer de exemplo que pode fazer o que você quer

 //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; } 

Exemplo:

 std::vector  v; Tokenizer s("split this string", " "); while (s.NextToken()) { v.push_back(s.GetToken()); } 

Esta é uma simples solução somente STL (~ 5 linhas!) Usando std::find e std::find_first_not_of que manipula repetições do delimitador (como espaços ou períodos, por exemplo), além de delimitadores iniciais e finais:

 #include  #include  void tokenize(std::string str, std::vector &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); } } 

Experimente ao vivo !

pystring é uma pequena biblioteca que implementa um monte de funções de string do Python, incluindo o método split:

 #include  #include  #include "pystring.h" std::vector chunks; pystring::split("this string", chunks); // also can specify a separator pystring::split("this-string", chunks, "-"); 

Eu postei esta resposta para uma pergunta semelhante.
Não reinvente a roda. Eu usei um número de bibliotecas e o mais rápido e mais flexível que eu já vi é: C ++ String Toolkit Library .

Aqui está um exemplo de como usá-lo que eu postei mais onde no stackoverflow.

 #include  #include  #include  #include  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 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 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; } 

Veja este exemplo. Pode te ajudar ..

 #include  #include  using namespace std; int main () { string tmps; istringstream is ("the dellimiter is the space"); while (is.good ()) { is >> tmps; cout < < tmps << "\n"; } return 0; } 

Você pode simplesmente usar uma biblioteca de expressão regular e resolver isso usando expressões regulares.

Use expression (\ w +) e a variável em \ 1 (ou $ 1 dependendo da implementação da biblioteca de expressões regulares).

Se você está disposto a usar C, você pode usar a function strtok . Você deve prestar atenção aos problemas de multi-thread ao usá-lo.

Para coisas simples eu uso apenas o seguinte:

 unsigned TokenizeString(const std::string& i_source, const std::string& i_seperators, bool i_discard_empty_tokens, std::vector& 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; } 

Aviso de responsabilidade covarde: Eu escrevo software de processamento de dados em tempo real onde os dados chegam por meio de arquivos binários, sockets ou alguma chamada de API (placas de E / S, câmeras). Eu nunca uso essa function para algo mais complicado ou crítico do que ler arquivos de configuração externos na boot.

MFC / ATL tem um tokenizer muito bom. Do 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 

Muitas sugestões excessivamente complicadas aqui. Experimente esta solução std :: string simples:

 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; } 

Eu pensei que era o que o operador >> em streams de strings era para:

 string word; sin >> word; 

A resposta de Adam Pierce fornece um tokenizador feito à mão levando um const char* . É um pouco mais problemático para fazer com iteradores porque incrementar o iterador final de uma string é indefinido . Dito isto, dado string str{ "The quick brown fox" } podemos certamente fazer isso:

 auto start = find(cbegin(str), cend(str), ' '); vector tokens{ string(cbegin(str), start) }; while (start != cend(str)) { const auto finish = find(++start, cend(str), ' '); tokens.push_back(string(start, finish)); start = finish; } 

Exemplo Vivo


Se você está procurando abstrair complexidade usando funcionalidade padrão, como On Freund sugere strtok é uma opção simples:

 vector tokens; for (auto i = strtok(data(str), " "); i != nullptr; i = strtok(nullptr, " ")) tokens.push_back(i); 

Se você não tiver access ao C ++ 17, precisará replace data(str) como neste exemplo: http://ideone.com/8kAGoa

Embora não seja demonstrado no exemplo, o strtok não precisa usar o mesmo delimitador para cada token. Junto com essa vantagem, existem várias desvantagens:

  1. strtok não pode ser usado em várias strings ao mesmo tempo: um nullptr deve ser passado para continuar o tokenizing da string atual ou um novo char* tokenize deve ser passado (existem algumas implementações não-padrão que suportam isto, no entanto, como : strtok_s )
  2. Pelo mesmo motivo strtok não pode ser usado em vários segmentos simultaneamente (isso pode, no entanto, ser implementação definida, por exemplo: implementação do Visual Studio é thread-safe )
  3. Chamar strtok modifica a string que está operando, portanto, não pode ser usado em const string , const char* s ou strings literais, para tokenizar qualquer um deles com strtok ou para operar em uma string cujos conteúdos precisam ser preservados, str teria que ser copiado, então a cópia poderia ser operada

Ambos os methods anteriores não podem gerar um vector tokenizado no local, ou seja, sem abstraí-los em uma function auxiliar, eles não podem inicializar os const vector tokens . Essa funcionalidade e a capacidade de aceitar qualquer delimitador de espaço em branco podem ser aproveitadas usando um istream_iterator . Por exemplo, dado: const string str{ "The quick \tbrown \nfox" } podemos fazer isto:

 istringstream is{ str }; const vector tokens{ istream_iterator(is), istream_iterator() }; 

Exemplo Vivo

A construção necessária de uma istringstream de istringstream para essa opção tem um custo muito maior do que as duas opções anteriores, no entanto, esse custo é normalmente oculto em detrimento da alocação de string de string .


Se nenhuma das opções acima for flexivel o suficiente para as suas necessidades de tokenização, a opção mais flexível é usar um regex_token_iterator claro, com essa flexibilidade vem uma despesa maior, mas novamente isso provavelmente está oculto no custo de alocação de string . Digamos, por exemplo, que queremos tokenizar com base em vírgulas não escapadas, também comendo espaço em branco, dada a seguinte input: const string str{ "The ,qu\\,ick ,\tbrown, fox" } podemos fazer isso:

 const regex re{ "\\s*((?:[^\\\\,]|\\\\.)*?)\\s*(?:,|$)" }; const vector tokens{ sregex_token_iterator(cbegin(str), cend(str), re, 1), sregex_token_iterator() }; 

Exemplo Vivo

Aqui está uma abordagem que permite controlar se os tokens vazios são incluídos (como strsep) ou excluídos (como strtok).

 #include  // for strchr and strlen /* * want_empty_tokens==true : include empty tokens, like strsep() * want_empty_tokens==false : exclude empty tokens, like strtok() */ std::vector tokenize(const char* src, char delim, bool want_empty_tokens) { std::vector 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; } 

Seems odd to me that with all us speed conscious nerds here on SO no one has presented a version that uses a compile time generated look up table for the delimiter (example implementation further down). Using a look up table and iterators should beat std::regex in efficiency, if you don’t need to beat regex, just use it, its standard as of C++11 and super flexible.

Some have suggested regex already but for the noobs here is a packaged example that should do exactly what the OP expects:

 std::vector split(std::string::const_iterator it, std::string::const_iterator end, std::regex e = std::regex{"\\w+"}){ std::smatch m{}; std::vector 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 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; } 

If we need to be faster and accept the constraint that all chars must be 8 bits we can make a look up table at compile time using metaprogramming:

 template struct BoolSequence{}; //just here to hold bools template struct CharSequence{}; //just here to hold chars template struct Contains; //generic template //not first specialization struct Contains,Match> : Contains, Match>{}; //strip first and increase index template //is first specialization struct Contains,First>: std::true_type {}; template //not found specialization struct Contains,Match>: std::false_type{}; template struct MakeSequence; //generic template struct MakeSequence, U>: //not last MakeSequence::value,Bs...>, U>{}; template struct MakeSequence<0,BoolSequence,U>{ //last using Type = BoolSequence; }; template struct BoolASCIITable; template struct BoolASCIITable>{ /* 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(c)]; } }; using Delims = CharSequence< '.',',',' ',':','\n'>; //list your custom delimiters here using Table = BoolASCIITable,Delims>::Type>; 

With that in place making a getNextToken function is easy:

 template std::pair 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); } 

Using it is also easy:

 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; } 

Here is a live example: http://ideone.com/GKtkLQ

There is no direct way to do this. Refer this code project source code to find out how to build a class for this.

you can take advantage of boost::make_find_iterator. Algo semelhante a isto:

 template inline vector< basic_string > tokenize( const basic_string &Input, const basic_string &Delimiter, bool remove_empty_token ) { typedef typename basic_string::const_iterator string_iterator_t; typedef boost::find_iterator< string_iterator_t > string_find_iterator_t; vector< basic_string > 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(it,i->begin())); } else Result.push_back(basic_string(it,i->begin())); it = i->end(); } if(it != it_end) Result.push_back(basic_string(it,it_end)); return Result; } 

If the maximum length of the input string to be tokenized is known, one can exploit this and implement a very fast version. I am sketching the basic idea below, which was inspired by both strtok() and the “suffix array”-data structure described Jon Bentley’s “Programming Perls” 2nd edition, chapter 15. The C++ class in this case only gives some organization and convenience of use. The implementation shown can be easily extended for removing leading and trailing whitespace characters in the tokens.

Basically one can replace the separator characters with string-terminating ‘\0’-characters and set pointers to the tokens withing the modified string. In the extreme case when the string consists only of separators, one gets string-length plus 1 resulting empty tokens. It is practical to duplicate the string to be modified.

Header file:

 class TextLineSplitter { public: TextLineSplitter( const size_t max_line_len ); ~TextLineSplitter(); void SplitLine( const char *line, const char sep_char = ',', ); inline size_t NumTokens( void ) const { return mNumTokens; } const char * GetToken( const size_t token_idx ) const { assert( token_idx < mNumTokens ); return mTokens[ token_idx ]; } private: const size_t mStorageSize; char *mBuff; char **mTokens; size_t mNumTokens; inline void ResetContent( void ) { memset( mBuff, 0, mStorageSize ); // mark all items as empty: memset( mTokens, 0, mStorageSize * sizeof( char* ) ); // reset counter for found items: mNumTokens = 0L; } }; 

Implementattion file:

 TextLineSplitter::TextLineSplitter( const size_t max_line_len ): mStorageSize ( max_line_len + 1L ) { // allocate memory mBuff = new char [ mStorageSize ]; mTokens = new char* [ mStorageSize ]; ResetContent(); } TextLineSplitter::~TextLineSplitter() { delete [] mBuff; delete [] mTokens; } void TextLineSplitter::SplitLine( const char *line, const char sep_char /* = ',' */, ) { assert( sep_char != '\0' ); ResetContent(); strncpy( mBuff, line, mMaxLineLen ); size_t idx = 0L; // running index for characters do { assert( idx < mStorageSize ); const char chr = line[ idx ]; // retrieve current character if( mTokens[ mNumTokens ] == NULL ) { mTokens[ mNumTokens ] = &mBuff[ idx ]; } // if if( chr == sep_char || chr == '\0' ) { // item or line finished // overwrite separator with a 0-terminating character: mBuff[ idx ] = '\0'; // count-up items: mNumTokens ++; } // if } while( line[ idx++ ] ); } 

A scenario of usage would be:

 // create an instance capable of splitting strings up to 1000 chars long: TextLineSplitter spl( 1000 ); spl.SplitLine( "Item1,,Item2,Item3" ); for( size_t i = 0; i < spl.NumTokens(); i++ ) { printf( "%s\n", spl.GetToken( i ) ); } 

saída:

 Item1 Item2 Item3 

boost::tokenizer is your friend, but consider making your code portable with reference to internationalization (i18n) issues by using wstring / wchar_t instead of the legacy string / char types.

 #include  #include  #include  using namespace std; using namespace boost; typedef tokenizer, wstring::const_iterator, wstring> Tok; int main() { wstring s; while (getline(wcin, s)) { char_separator sep(L" "); // list of separator characters Tok tok(s, sep); for (Tok::iterator beg = tok.begin(); beg != tok.end(); ++beg) { wcout < < *beg << L"\t"; // output (or store in vector) } wcout << L"\n"; } return 0; } 

Simple C++ code (standard C++98), accepts multiple delimiters (specified in a std::string), uses only vectors, strings and iterators.

 #include  #include  #include  #include  std::vector split(const std::string& str, const std::string& delim){ std::vector result; if (str.empty()) throw std::runtime_error("Can not tokenize an empty string!"); std::string::const_iterator begin, str_it; begin = str_it = str.begin(); do { while (delim.find(*str_it) == std::string::npos && str_it != str.end()) str_it++; // find the position of the first delimiter in str std::string token = std::string(begin, str_it); // grab the token if (!token.empty()) // empty token only when str starts with a delimiter result.push_back(token); // push the token into a vector while (delim.find(*str_it) != std::string::npos && str_it != str.end()) str_it++; // ignore the additional consecutive delimiters begin = str_it; // process the remaining tokens } while (str_it != str.end()); return result; } int main() { std::string test_string = ".this is.a.../.simple;;test;;;END"; std::string delim = "; ./"; // string containing the delimiters std::vector tokens = split(test_string, delim); for (std::vector::const_iterator it = tokens.begin(); it != tokens.end(); it++) std::cout < < *it << std::endl; } 
 /// split a string into multiple sub strings, based on a separator string /// for example, if separator="::", /// /// s = "abc" -> "abc" /// /// s = "abc::def xy::st:" -> "abc", "def xy" and "st:", /// /// s = "::abc::" -> "abc" /// /// s = "::" -> NO sub strings found /// /// s = "" -> NO sub strings found /// /// then append the sub-strings to the end of the vector v. /// /// the idea comes from the findUrls() function of "Accelerated C++", chapt7, /// findurls.cpp /// void split(const string& s, const string& sep, vector& v) { typedef string::const_iterator iter; iter b = s.begin(), e = s.end(), i; iter sep_b = sep.begin(), sep_e = sep.end(); // search through s while (b != e){ i = search(b, e, sep_b, sep_e); // no more separator found if (i == e){ // it's not an empty string if (b != e) v.push_back(string(b, e)); break; } else if (i == b){ // the separator is found and right at the beginning // in this case, we need to move on and search for the // next separator b = i + sep.length(); } else{ // found the separator v.push_back(string(b, i)); b = i; } } } 

The boost library is good, but they are not always available. Doing this sort of things by hand is also a good brain exercise. Here we just use the std::search() algorithm from the STL, see the above code.