Dividere una stringa in parole di delimitatori multipli

Ho del testo (testo significativo o espressione aritmetica) e voglio dividerlo in parole.
Se avessi un singolo delimitatore, userei:

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

Come posso rompere la stringa in token con diversi delimitatori?

Supponendo che uno dei delimitatori sia newline, quanto segue legge la riga e la divide ulteriormente dai delimitatori. Per questo esempio ho scelto lo spazio delimitatore, l’apostrofo e il punto e virgola.

 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 hai boost, puoi usare:

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

Non so perché nessuno ha indicato il modo manuale, ma eccolo qui:

 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 in funzione:

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

In questo modo puoi fare qualcosa di utile con le delimitazioni se vuoi.

Se sei interessante su come farlo da solo e non usare boost.

Supponendo che la stringa del delimitatore possa essere molto lunga – diciamo che M, controllando ogni carattere nella stringa se è un delimitatore, costerebbe O (M) ciascuno, così facendo in un ciclo per tutti i caratteri nella stringa originale, diciamo di lunghezza N, è O (M * N).

Vorrei usare un dizionario (come una mappa – “delimitatore” per “booleani” – ma qui userei un semplice array booleano che ha valore true in index = ascii per ogni delimitatore).

Ora, iterando sulla stringa e verificando se il char è un delimitatore è O (1), che alla fine ci dà O (N) nel complesso.

Ecco il mio codice di esempio:

 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 restituisce il vettore in base al valore e lo crea prima nello stack, quindi stiamo utilizzando qui la potenza del compilatore >>> RVO - Ottimizzazione del valore di ritorno 🙂