web-dev-qa-db-fra.com

Fractionner une chaîne en mots par plusieurs délimiteurs

J'ai du texte (texte explicite ou expression arithmétique) et je souhaite le scinder en mots.
Si j'avais un seul délimiteur, j'utiliserais: 

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

Comment puis-je briser la chaîne en jetons avec plusieurs délimiteurs? 

30
wzxsvm

En supposant que l’un des délimiteurs soit newline, ce qui suit lit la ligne et la scinde par la suite. Pour cet exemple, j'ai choisi les délimiteurs espace, apostrophe et point-virgule.

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

Si vous avez un boost, vous pouvez utiliser:

#include <boost/algorithm/string.hpp>
std::string inputString("One!Two,Three:Four");
std::string delimiters("|,:");
std::vector<std::string> parts;
boost::split(parts, inputString, boost::is_any_of(delimiters));
18
MattSmith

Je ne sais pas pourquoi personne n'a signalé le mode manuel, mais le voici:

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

et en fonction:

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

De cette façon, vous pouvez faire quelque chose d'utile avec les delims si vous voulez.

4
forumulator

Si vous souhaitez savoir comment le faire vous-même et ne pas utiliser de boost.

En supposant que la chaîne de délimiteur puisse être très longue - disons M, vérifier pour chaque caractère de votre chaîne s’il s’agit d’un délimiteur, coûterait O(M) chacun. chaîne, disons en longueur N, est O (M * N).

Je voudrais utiliser un dictionnaire (comme une carte - "délimiteur" à "booléens" - mais ici, je voudrais utiliser un tableau booléen simple qui a la valeur true dans index = valeur ascii pour chaque délimiteur).

À présent, itérer sur la chaîne et vérifier si le caractère est un délimiteur est O (1), ce qui nous donne finalement O(N).

Voici mon exemple de code:

const int dictSize = 256;    

vector<string> tokenizeMyString(const string &s, const string &del)
{
    static bool dict[dictSize] = { false};

    vector<string> 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<string> res = tokenizeMyString(delString, " ,");

    for (auto &i : res) {

        cout << "token: " << i << endl;
    }
return 0;
}

Remarque: tokenizeMyString renvoie le vecteur par valeur et le crée d'abord sur la pile. Nous utilisons donc ici la puissance du compilateur >>> RVO - optimisation de la valeur renvoyée :) 

0
Kohn1001