¿Cómo puedo tokenizar una cadena en C ++?

Java tiene un método de división conveniente:

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

¿Hay alguna manera fácil de hacer esto en C ++?

Su caso simple puede construirse fácilmente usando el método std::string::find . Sin embargo, eche un vistazo a Boost.Tokenizer . Es genial. Boost generalmente tiene algunas herramientas de cuerdas muy geniales.

La clase Boost tokenizer puede hacer que este tipo de cosas sea bastante simple:

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

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

Aquí hay uno muy simple:

 #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. En mi opinión, no hay necesidad de construir una clase sobre tokenización a menos que strtok no te proporcione lo que necesitas. Puede que no, pero en más de 15 años escribiendo varios códigos de análisis en C y C ++, siempre he usado strtok. Aquí hay un ejemplo

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

Algunas advertencias (que pueden no satisfacer sus necesidades). La cadena se “destruye” en el proceso, lo que significa que los caracteres EOS se colocan en línea en los puntos delimitados. El uso correcto puede requerir que hagas una versión no const de la cadena. También puede cambiar la lista de delimitadores a medio analizar.

En mi opinión, el código anterior es mucho más simple y fácil de usar que escribir una clase separada para él. Para mí, esta es una de esas funciones que proporciona el lenguaje y lo hace bien y limpiamente. Es simplemente una solución “basada en C”. Es apropiado, es fácil y no tienes que escribir mucho código adicional 🙂

Otra manera rápida es usar getline . Algo como:

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

Si lo desea, puede hacer que un método split() simple devuelva un vector , que es realmente útil.

Puede usar flujos, iteradores y el algoritmo de copia para hacer esto de manera bastante directa.

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

Sin ánimo de ofender, pero por un problema tan simple, estás haciendo las cosas demasiado complicadas. Hay muchas razones para usar Boost . Pero por algo tan simple, es como golpear una mosca con un trineo de 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 ejemplo (para el 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] ); } 

Y sí, podríamos haber dividido () devolver un nuevo vector en lugar de pasar uno. Es trivial envolver y sobrecargar. Pero dependiendo de lo que estoy haciendo, a menudo me parece mejor reutilizar objetos preexistentes en lugar de crear siempre otros nuevos. (¡Siempre y cuando no olvides vaciar el vector en el medio!)

Referencia: http://www.cplusplus.com/reference/string/string/ .

(Originalmente estaba escribiendo una respuesta a la pregunta de Doug: cadenas de C ++ modificando y extrayendo en base a separadores (cerrado) . Pero dado que Martin York cerró esa pregunta con un puntero aquí ... voy a generalizar mi código).

Boost tiene una fuerte función de división: boost :: algorithm :: split .

Progtwig de ejemplo:

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

Salida:

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

Una solución que usa 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; } } 

Sé que solicitó una solución de C ++, pero podría considerar esto útil:

Qt

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

La ventaja sobre Boost en este ejemplo es que se trata de un mapeo uno a uno directo al código de su publicación.

Ver más en la documentación de Qt

Aquí hay una clase de tokenizer de muestra que puede hacer lo que quieras

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

Ejemplo:

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

Esta es una solución simple solo STL (~ 5 líneas!) Usando std::find y std::find_first_not_of que maneja las repeticiones del delimitador (como espacios o períodos, por ejemplo), así como los delimitadores iniciales y finales:

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

Pruébalo en vivo !

pystring es una pequeña biblioteca que implementa un conjunto de funciones de cadena de Python, incluido el método de división:

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

Publiqué esta respuesta para una pregunta similar.
No reinventar la rueda. He utilizado varias bibliotecas y la más rápida y flexible que he encontrado es la biblioteca C ++ String Toolkit .

Aquí hay un ejemplo de cómo usarlo que he publicado en otro lugar en el 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; } 

Verifique este ejemplo. Podría ayudarte …

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

Simplemente puede usar una biblioteca de expresiones regulares y resolver eso usando expresiones regulares.

Use expresión (\ w +) y la variable en \ 1 (o $ 1 dependiendo de la implementación de la biblioteca de expresiones regulares).

Si está dispuesto a usar C, puede usar la función strtok . Debe prestar atención a los problemas de subprocesos múltiples al usarlo.

Para cosas simples solo uso lo siguiente:

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

Descargo de responsabilidad cobarde: escribo software de procesamiento de datos en tiempo real donde los datos vienen a través de archivos binarios, sockets o algunas llamadas API (tarjetas de E / S, cámaras). Nunca utilizo esta función para algo más complicado o crítico que leer archivos de configuración externos al inicio.

MFC / ATL tiene un muy buen tokenizador. Desde 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 

Muchas sugerencias demasiado complicadas aquí. Pruebe esta solución simple 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; } 

Pensé que eso era para lo que era el operador >> en secuencias de cadenas:

 string word; sin >> word; 

La respuesta de Adam Pierce proporciona un tokenizador hilado a mano que contiene un const char* . Es un poco más problemático hacer con iteradores porque incrementar el iterador final de una string no está definido . Dicho esto, dada la string str{ "The quick brown fox" } ciertamente podemos lograr esto:

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

Ejemplo en vivo


Si busca abstraer la complejidad mediante el uso de funcionalidad estándar, como On Freund sugiere que strtok es una opción simple:

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

Si no tiene acceso a C ++ 17, deberá sustituir los data(str) como en este ejemplo: http://ideone.com/8kAGoa

Aunque no se demostró en el ejemplo, strtok no necesita usar el mismo delimitador para cada token. Junto con esta ventaja, sin embargo, hay varios inconvenientes:

  1. strtok no se puede usar en varias strings al mismo tiempo: se debe pasar un nullptr para seguir tokenizando la string actual o se debe pasar una nueva char* a tokenize (hay algunas implementaciones no estándar que sí lo admiten, como : strtok_s )
  2. Por la misma razón, strtok no se puede usar en varios subprocesos simultáneamente (sin embargo, esto puede ser una implementación definida, por ejemplo: la implementación de Visual Studio es segura para subprocesos )
  3. Llamar a strtok modifica la string que está operando, por lo que no se puede usar en const string s, const char* s o literal, para tokenizar cualquiera de estos con strtok o para operar en una string cuyos contenidos deben conservarse, str tendría que ser copiado, entonces la copia podría ser operada

Los dos métodos anteriores no pueden generar un vector tokenizado in situ, lo que significa que, sin abstraerlos en una función auxiliar, no pueden inicializar const vector tokens . Esa funcionalidad y la capacidad de aceptar cualquier delimitador de espacio en blanco se pueden aprovechar utilizando un istream_iterator . Por ejemplo, dado: const string str{ "The quick \tbrown \nfox" } podemos hacer esto:

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

Ejemplo en vivo

La construcción requerida de un istringstream para esta opción tiene un costo mucho mayor que las 2 opciones anteriores, sin embargo, este costo generalmente se oculta en el gasto de la asignación de string .


Si ninguna de las opciones anteriores es lo suficientemente flexible para sus necesidades de tokenización, la opción más flexible es utilizar un regex_token_iterator por supuesto, con esta flexibilidad viene un mayor gasto, pero de nuevo es probable que esté oculto en el costo de asignación de string . Digamos, por ejemplo, que queremos hacer tokens basados ​​en comas no escapadas, también comiendo espacios en blanco, con la siguiente entrada: const string str{ "The ,qu\\,ick ,\tbrown, fox" } podemos hacer esto:

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

Ejemplo en vivo

Aquí hay un enfoque que le permite controlar si se incluyen tokens vacíos (como strsep) o excluidos (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; } 

Me parece extraño que con todos nosotros los nerds conscientes de la velocidad aquí en SO, nadie ha presentado una versión que utiliza una tabla de búsqueda generada en tiempo de comstackción para el delimitador (ejemplo de implementación más abajo). El uso de una tabla de búsqueda e iteradores debe superar a std :: regex en eficiencia, si no necesita vencer la expresión regular, simplemente úselo, es estándar a partir de C ++ 11 y súper 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. Something similar to this:

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

salida:

 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.