Como faço para toquiar um texto Em C++?

Java tem um método split conveniente:

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

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

Author: DavidRR, 2008-09-10

30 answers

O seu caso simples pode ser facilmente construído usando o std::string::find Método. No entanto, dê uma olhada em impulso.Tokenizer . É óptimo. Boost geralmente tem algumas ferramentas de corda muito legal.

 132
Author: Konrad Rudolph, 2013-06-07 14:36:19

A classe Boost tokenizer pode tornar este tipo de coisas bastante simples:

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

Actualizado para 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;
    }
}
 182
Author: Ferruccio, 2012-05-25 13:12:19
Aqui está uma muito simples:
#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;
}
 161
Author: Adam Pierce, 2012-12-19 22:21:45

Usa o strtok. Na minha opinião, não há necessidade de construir uma classe em torno de tokenizing a menos que strtok não lhe fornece 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 satisfazer as suas necessidades). A cadeia é "destruída" no processo, o que significa que os caracteres EOS são colocados em linha nos pontos delimitadores. Uma utilização correcta pode exigir que faça um versão não-const da string. Você também pode alterar a lista de Delimitadores mid parse.

Na minha opinião, o código acima é muito mais simples e fácil de usar do que escrever uma classe separada para ele. Para mim, esta é uma daquelas funções que a linguagem fornece e faz bem e limpo. É simplesmente uma solução baseada em "C". É apropriado, é fácil, e você não tem que escrever um monte de código extra: -)

 102
Author: Mark, 2008-09-10 13:37:33

Outra maneira rápida é usar getline. Algo do género:

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<string>, que é util.

 90
Author: user35978, 2010-03-04 12:12:21

Você pode usar fluxos, iteradores, e o algoritmo de cópia para fazer isso diretamente.

#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);
}
 80
Author: KeithB, 2008-09-10 12:46:14
Sem ofensa pessoal, mas para um problema tão simples, estão a fazer coisas. Caminho demasiado complicado. Há muitas razões para usar impulso . Mas para algo tão simples, é como bater numa mosca com um trenó de 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());
    }
}

Por exemplo (para o caso do Doug),

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

E Sim, poderíamos ter dividido() retornar um novo vetor em vez de passar um para dentro. É trivial embrulhar e sobrecarregar. Mas dependendo do que estou a fazer, muitas vezes acho melhor reutilizar objetos pré-existentes em vez de sempre criar novos. (Desde que não me esqueça de esvaziar o vector no meio!)

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

(estava originalmente a escrever uma resposta à pergunta do Doug: Cadeias de C++ A Modificar e a extrair com base em separadores (fechados). Mas desde que Martin York fechou essa questão com um ponteiro aqui... Vou generalizar o meu código.)

 46
Author: Mr.Ree, 2017-05-23 11:47:32

O Boost tem uma função de divisão forte: impulso:: algoritmo: split .

Programa de amostragem:

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

Resultado:

"a"
"b"
" c "
""
"e"
"f"
""
 34
Author: Raz, 2014-11-12 00:01:16

Uma solução utilizando 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;
    }
}
 25
Author: w.b, 2014-12-14 10:46:47

Sei que pediste uma solução C++, mas podes considerar isto útil:

Qt

#include <QString>

...

QString str = "The quick brown fox"; 
QStringList results = str.split(" "); 
A vantagem sobre o impulso neste exemplo é que é um mapeamento direto de um para um para o código do seu post.

Ver mais documentação em Qt

 23
Author: sivabudh, 2018-04-10 15:51:32

Aqui está uma classe de tokenizer de amostra 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 <std::string> v;
Tokenizer s("split this string", " ");
while (s.NextToken())
{
    v.push_back(s.GetToken());
}
 22
Author: vzczc, 2015-07-13 06:48:11

Esta é uma solução simples só para STL (~5 linhas!) usando std::find e std::find_first_not_of que lida com as repetições do delimitador( como espaços ou períodos, por exemplo), bem como delimitadores de liderança e trilho:

#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);
    }
}
Experimenta. vivos!
 18
Author: Parham, 2015-03-01 22:18:06

O Pystring é uma pequena biblioteca que implementa um conjunto de funções de texto do Python, incluindo o método 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, "-");
 16
Author: dbr, 2011-12-29 15:17:58
Postei esta resposta para uma pergunta semelhante.
Não reinventes a roda. Eu usei uma série de bibliotecas e a mais rápida e flexível que eu encontrei é: biblioteca de ferramentas de Texto C++ . Aqui está um exemplo de como usá-lo que eu postei no stackoverflow.
#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;
}
 9
Author: DannyK, 2018-04-17 18:49:19

Veja este exemplo. Pode ajudar-te..

#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;
}
 8
Author: sohesado, 2010-12-20 12:25:35

Pode simplesmente usar uma biblioteca de Expressões Regularese resolvê-la usando expressões regulares.

Usar a expressão (\w+) e a variável em \1 (ou $1, dependendo da implementação da biblioteca de Expressões Regulares).

 5
Author: Fawix, 2012-02-27 16:15:21

Se estiver disposto a usar C, pode usar a função strtok. Você deve prestar atenção aos problemas multi-threading ao usá-lo.

 4
Author: On Freund, 2008-09-10 12:23:33

Para coisas simples só uso o seguinte:

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

Disclaimer covarde: eu escrevo software de processamento de dados em tempo real onde os dados vêm através de arquivos binários, tomadas, ou alguma chamada API (cartões de E/S, câmeras). Eu nunca uso Esta função para algo mais complicado ou crítico de tempo do que ler arquivos de configuração externa no arranque.

 4
Author: jilles de wit, 2008-09-15 15:28:39

O 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
 4
Author: Jim In Texas, 2009-03-22 02:28:16
Muitas sugestões demasiado complicadas. Tente este std simples:: solução de texto:
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;
}
 4
Author: David919, 2012-08-01 05:50:39

Eu pensei que era para isso que o operador >> em cadeias de caracteres era:

string word; sin >> word;
 4
Author: Daren Thomas, 2013-09-10 23:38:39

A resposta de Adam Pierce fornece um tokenizer girado à mão tomando um const char*. É um pouco mais problemático para os iteradores porque o aumento do iterador final de é indefinido . Dito isto, dado que podemos certamente fazer isto:

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

Exemplo Vivo


Se você está olhando para a complexidade abstrata usando a funcionalidade padrão, como No Freund sugere strtok é uma opção simples:

vector<string> tokens;

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

Se não tiver acesso a c++17 terá de substituir data(str) Como neste exemplo: http://ideone.com/8kAGoa

Embora não esteja demonstrado no exemplo, strtok não é necessário utilizar o mesmo separador para cada símbolo. Juntamente com esta vantagem, no entanto, há vários inconvenientes:

  1. strtok não pode ser usado em várias strings ao mesmo tempo: Ou um nullptr deve ser passado para continuar a simbolização atual string ou um novo char* para indexar deve be passed (there are some non-standard implementations which do support this however, such as: strtok_s)
  2. pela mesma razão strtok não pode ser usado em vários tópicos simultaneamente (isto pode, no entanto, ser definido como implementação, por exemplo: a implementação do Visual Studio é segura por thread)
  3. chamando strtok modifica o {[5] } em que está a operar, por isso não pode ser usado em const string s, const char* s, ou cadeias literais, para fazer qualquer uma destas com strtok ou para operar em a string que o conteúdo precisa ser preservado, str teria que ser copiado, então a cópia poderia ser operada em

Ambos os métodos anteriores não conseguem gerar um tokenized vector no local, o que significa que sem os abstrair numa função auxiliar eles não podem inicializar const vector<string> tokens. Essa funcionalidade e a capacidade de aceitar qualquer delimitador de espaço branco pode ser aproveitada usando um istream_iterator. Por exemplo: {[27] } podemos fazer isto:

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

Exemplo Vivo

A construção necessária de um istringstream para esta opção tem um custo muito maior do que as 2 opções anteriores, no entanto, este custo é normalmente escondido na despesa de string alocação.


Se nenhuma das opções acima são flexíveis o suficiente para as suas necessidades de tokenization, a opção mais flexível é usar um regex_token_iterator é claro que com esta flexibilidade vem uma despesa maior, mas mais uma vez isso é provavelmente escondido em o custo de afectação string. Digamos, por exemplo, que queremos tokenize com base em vírgulas não-escapadas, também comendo espaço-branco, dada a seguinte entrada: {[32] } podemos fazer isto:

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

Exemplo Vivo

 4
Author: Jonathan Mee, 2017-09-22 21:45:01

Aqui está uma abordagem que lhe permite controlar se os tokens vazios estão incluídos (como o strsep) ou excluídos (como o 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;
}
 2
Author: Darren Smith, 2012-11-11 07:22:43
{[[6]} parece-me estranho que com todos os nerds conscientes de velocidade dos EUA aqui em diante, para que ninguém tenha apresentado uma versão que usa uma tabela de pesquisa do delimitador gerada pelo tempo de compilação (exemplo implementação mais abaixo). Usando uma tabela de pesquisa e iteradores deve bater std:: regex em eficiência, se você não precisa bater regex, basta usá-lo, seu padrão a partir de C++11 e super flexível.

Alguns já sugeriram regex mas para os noobs aqui está um exemplo embalado que deve fazer exatamente o que o OP espera:

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

Se precisarmos de ser mais rápidos e aceitar a restrição de que todos os caracteres devem ser de 8 bits, podemos fazer uma tabela de pesquisa no tempo de compilação usando o metaprogramação:

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

Com isso no lugar, fazer uma função getNextToken é fácil:

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

Usar também é fácil:

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;
}
Aqui está um exemplo vivo: http://ideone.com/GKtkLQ
 2
Author: odinthenerd, 2014-07-27 20:06:01
Não há maneira directa de fazer isto. Consulte este código fonte do projecto para saber como criar uma classe para isto.
 1
Author: Niyaz, 2008-09-10 12:14:59

Você pode tirar vantagem do boost:: make_find_iterator. Algo semelhante a isto:

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;
}
 1
Author: Arash, 2011-08-03 06:58:13

Se o comprimento máximo da cadeia de entrada a ser tokenized é conhecido, pode-se explorar isto e implementar uma versão muito rápida. Estou esboçando a idéia básica abaixo, que foi inspirada por strtok () e a estrutura de dados do sufixo descreve a segunda edição de"Programming Perls "de Jon Bentley, Capítulo 15. A Classe C++ neste caso só dá Alguma Organização e conveniência de uso. A implementação mostrada pode ser facilmente estendida para remover o espaço em branco de liderança e trilho personagens nas fichas.

Basicamente, pode-se substituir os caracteres separadores por Caracteres de terminação de texto '\0'-caracteres e definir ponteiros para os itens com a sequência modificada. No caso extremo, quando a cadeia consiste apenas de separadores, obtém-se comprimento de cadeia mais 1 tokens vazios resultantes. É prático duplicar a string a ser modificada.

Ficheiro Header:

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

Ficheiro de implementação:

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

Um cenário de Utilização seria 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 ) );
}

Resultado:

Item1

Item2
Item3
 0
Author: Angel Sinigersky, 2011-05-15 20:47:07

boost::tokenizer é seu amigo, mas considere fazer o seu código portátil com referência a questões de internacionalização (i18n), usando wstring/wchar_t em vez do legado string/char tipos.

#include <iostream>
#include <boost/tokenizer.hpp>
#include <string>

using namespace std;
using namespace boost;

typedef tokenizer<char_separator<wchar_t>,
                  wstring::const_iterator, wstring> Tok;

int main()
{
  wstring s;
  while (getline(wcin, s)) {
    char_separator<wchar_t> 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;
}
 0
Author: jochenleidner, 2012-07-16 01:19:26

Código C++ simples (norma C++98), aceita vários delimitadores (especificados num std: string), usa apenas Vectores, cadeias de caracteres e iteradores.

#include <iostream>
#include <vector>
#include <string>
#include <stdexcept> 

std::vector<std::string> 
split(const std::string& str, const std::string& delim){
    std::vector<std::string> 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<string>
        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<std::string> tokens = split(test_string, delim);           
    for (std::vector<std::string>::const_iterator it = tokens.begin(); 
        it != tokens.end(); it++)
            std::cout << *it << std::endl;
}
 0
Author: vsoftco, 2013-12-15 01:32:53
/// 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<string>& 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;
        }
    }
}
A biblioteca boost é boa, mas nem sempre estão disponíveis. Fazer este tipo de coisas à mão também é um bom exercício cerebral. Aqui nós apenas usamos o algoritmo std::search() do STL, veja o código acima.
 0
Author: Murphy78, 2014-02-25 06:40:51