Diviser une chaîne en mots par plusieurs délimiteurs
J'ai du texte (texte significatif ou expression arithmétique) et je veux le diviser 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 casser la chaîne en jetons avec plusieurs délimiteurs?
4 réponses
En supposant que l'un des délimiteurs est newline, ce qui suit lit la ligne et la divise davantage par les délimiteurs. 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));
}
Si vous avez 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));
Je ne sais pas pourquoi personne n'a souligné la manière manuelle, 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 délimitations si vous le souhaitez.
Si vous intéressant dans la façon de le faire vous-même et ne pas utiliser boost.
En supposant que la chaîne de délimiteur peut être très longue-disons M, en vérifiant tous les caractères de votre chaîne s'il s'agit d'un délimiteur, cela coûterait O (M) chacun, donc dans une boucle pour tous les caractères de votre chaîne d'origine, disons en longueur N, est O (M * N).
J'utiliserais un dictionnaire (comme une carte - "délimiteur" à "booléens" - mais ici j'utiliserais un simple tableau booléen qui a true dans index = ASCII valeur pour chacun délimiteur).
Maintenant itérer sur la chaîne et vérifier si le char est un délimiteur est O (1), ce qui nous donne finalement O(N) global.
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;
}
Note: tokenizeMyString renvoie Le Vecteur par valeur et le crée d'abord sur la pile, donc nous utilisons ici la puissance du compilateur > > > RVO - return value optimization:)