Dividir uma string em palavras por vários delimitadores

Eu tenho algum texto (texto significativo ou expressão aritmética) e quero dividi-lo em palavras.
Se eu tivesse um único delimitador, usaria:

std::stringstream stringStream(inputString); std::string word; while(std::getline(stringStream, word, delimiter)) { wordVector.push_back(word); } 

Como posso quebrar a string em tokens com vários delimitadores?

Supondo que um dos delimitadores é nova linha, o seguinte lê a linha e a divide ainda mais pelos delimitadores. Para este exemplo, escolhi o espaço delimitador, apóstrofo e ponto e vírgula.

 std::stringstream stringStream(inputString); std::string line; while(std::getline(stringStream, line)) { std::size_t prev = 0, pos; while ((pos = line.find_first_of(" ';", prev)) != std::string::npos) { if (pos > prev) wordVector.push_back(line.substr(prev, pos-prev)); prev = pos+1; } if (prev < line.length()) wordVector.push_back(line.substr(prev, std::string::npos)); } 

Se você tem impulso, você poderia usar:

 #include  std::string inputString("One!Two,Three:Four"); std::string delimiters("|,:"); std::vector parts; boost::split(parts, inputString, boost::is_any_of(delimiters)); 

Eu não sei porque ninguém apontou o caminho manual, mas aqui está:

 const std::string delims(";,:. \n\t"); inline bool isDelim(char c) { for (int i = 0; i < delims.size(); ++i) if (delims[i] == c) return true; return false; } 

e em function:

 std::stringstream stringStream(inputString); std::string word; char c; while (stringStream) { word.clear(); // Read word while (!isDelim((c = stringStream.get()))) word.push_back(c); if (c != EOF) stringStream.unget(); wordVector.push_back(word); // Read delims while (isDelim((c = stringStream.get()))); if (c != EOF) stringStream.unget(); } 

Desta forma, você pode fazer algo útil com os delims, se quiser.

Se você é interessante em como fazê-lo sozinho e não usando o impulso.

Assumindo que a string delimitadora pode ser muito longa – digamos que M, checando cada caractere em sua string se for um delimitador, custaria O (M) cada, fazendo isso em um loop para todos os caracteres em sua string original, por exemplo no comprimento N, é O (M * N).

Eu usaria um dictionary (como um mapa – “delimitador” para “booleanos” – mas aqui eu usaria um array booleano simples que tenha true em index = ascii value para cada delimitador).

Agora iterar na string e verificar se o char é um delimitador é O (1), o que eventualmente nos dá O (N) como um todo.

Aqui está o meu código de exemplo:

 const int dictSize = 256; vector tokenizeMyString(const string &s, const string &del) { static bool dict[dictSize] = { false}; vector res; for (int i = 0; i < del.size(); ++i) { dict[del[i]] = true; } string token(""); for (auto &i : s) { if (dict[i]) { if (!token.empty()) { res.push_back(token); token.clear(); } } else { token += i; } } if (!token.empty()) { res.push_back(token); } return res; } int main() { string delString = "MyDog:Odie, MyCat:Garfield MyNumber:1001001"; //the delimiters are " " (space) and "," (comma) vector res = tokenizeMyString(delString, " ,"); for (auto &i : res) { cout << "token: " << i << endl; } return 0; } 

Nota: tokenizeMyString retorna vetor por valor e o cria na pilha primeiro, então estamos usando aqui o poder do compilador >>> RVO - return value optimization 🙂