574 votes

Analyser (diviser) une chaîne en C++ en utilisant le délimiteur de chaîne (C++ standard)

J'analyse une chaîne de caractères en C++ en utilisant la méthode suivante :

using namespace std;

string parsed,input="text to be parsed";
stringstream input_stringstream(input);

if (getline(input_stringstream,parsed,' '))
{
     // do some processing.
}

L'analyse syntaxique avec un délimiteur à caractère unique est correcte. Mais que faire si je veux utiliser une chaîne de caractères comme délimiteur.

Exemple : Je veux me séparer :

scott>=tiger

avec >= comme délimiteur pour que je puisse avoir scott et tiger.

2 votes

stackoverflow.blog/2019/10/11/ Descendez jusqu'au numéro 5.

34voto

Hossein Points 1789

Vous pouvez également utiliser une expression rationnelle pour cela :

std::vector<std::string> split(const std::string str, const std::string regex_str)
{
    std::regex regexz(regex_str);
    std::vector<std::string> list(std::sregex_token_iterator(str.begin(), str.end(), regexz, -1),
                                  std::sregex_token_iterator());
    return list;
}

ce qui est équivalent à :

std::vector<std::string> split(const std::string str, const std::string regex_str)
{
    std::sregex_token_iterator token_iter(str.begin(), str.end(), regexz, -1);
    std::sregex_token_iterator end;
    std::vector<std::string> list;
    while (token_iter != end)
    {
        list.emplace_back(*token_iter++);
    }
    return list;
}

et l'utiliser comme ceci :

#include <iostream>
#include <string>
#include <regex>

std::vector<std::string> split(const std::string str, const std::string regex_str)
{   // a yet more concise form!
    return { std::sregex_token_iterator(str.begin(), str.end(), std::regex(regex_str), -1), std::sregex_token_iterator() };
}

int main()
{
    std::string input_str = "lets split this";
    std::string regex_str = " "; 
    auto tokens = split(input_str, regex_str);
    for (auto& item: tokens)
    {
        std::cout<<item <<std::endl;
    }
}

jouez avec lui en ligne ! http://cpp.sh/9sumb

vous pouvez simplement utiliser des sous-chaînes, des caractères, etc. comme d'habitude, ou utiliser des expressions régulières pour effectuer le fractionnement.
Il est également concis et C++11 !

2 votes

Cela devrait être la bonne réponse, à condition que C++11 soit sur la table. Si ce n'est pas le cas... vous devriez utiliser C++>=11, cela change la donne !

0 votes

Pouvez-vous expliquer l'instruction de retour dans la fonction split() ? J'essaye de comprendre comment les jetons sont poussés dans les std::vector conteneur. Merci.

0 votes

L'écrire comme return std::vector<std::string>{ std::sregex_token_iterator(str.begin(), str.end(), std::regex(regex_str), -1), std::sregex_token_iterator() }; pour que vous compreniez mieux comment un vecteur temporaire std::vector est créé et renvoyé ? nous utilisons l'initialisation de liste ici. jetez un coup d'oeil aquí

22voto

William Cuervo Points 240

Ce code sépare les lignes du texte, et les ajoute toutes dans un vecteur.

vector<string> split(char *phrase, string delimiter){
    vector<string> list;
    string s = string(phrase);
    size_t pos = 0;
    string token;
    while ((pos = s.find(delimiter)) != string::npos) {
        token = s.substr(0, pos);
        list.push_back(token);
        s.erase(0, pos + delimiter.length());
    }
    list.push_back(s);
    return list;
}

Appelé par :

vector<string> listFilesMax = split(buffer, "\n");

0 votes

Ça marche très bien ! J'ai ajouté list.push_back(s) ; car il manquait.

1 votes

Il manque la dernière partie de la chaîne. Après la fin de la boucle while, nous devons ajouter le reste de s comme un nouveau jeton.

0 votes

J'ai fait une modification de l'exemple de code pour corriger le push_back manquant.

19voto

ryanbwork Points 1506

strtok vous permet de passer plusieurs caractères comme délimiteurs. Je parie que si vous passiez par ">=", la chaîne de votre exemple serait divisée correctement (même si les caractères > et = sont comptés comme des délimiteurs individuels).

EDIT si vous ne voulez pas utiliser c_str() pour convertir une chaîne de caractères en char*, vous pouvez utiliser substrat et trouver le premier de pour le tokenize.

string token, mystring("scott>=tiger");
while(token != mystring){
  token = mystring.substr(0,mystring.find_first_of(">="));
  mystring = mystring.substr(mystring.find_first_of(">=") + 1);
  printf("%s ",token.c_str());
}

3 votes

Merci. Mais je veux utiliser uniquement le C++ et non pas des fonctions C telles que strtok() car cela m'obligerait à utiliser un tableau de caractères au lieu d'une chaîne de caractères.

2 votes

@TheCrazyProgrammer Alors ? Si une fonction C fait ce dont vous avez besoin, utilisez-la. Nous ne sommes pas dans un monde où les fonctions C ne sont pas disponibles en C++ (en fait, elles doivent l'être). .c_str() est bon marché et facile, aussi.

1 votes

La vérification de if(token != mystring) donne des résultats erronés si vous avez des éléments répétitifs dans votre chaîne. J'ai utilisé votre code pour créer une version qui n'a pas ce problème. Elle comporte de nombreux changements qui modifient fondamentalement la réponse, c'est pourquoi j'ai écrit ma propre réponse au lieu de la modifier. Consultez-la ci-dessous.

13voto

Shubham Agrawal Points 118

La réponse est déjà là, mais la réponse sélectionnée utilise la fonction d'effacement qui est très coûteuse, pensez à une très grosse chaîne (en Mo). C'est pourquoi j'utilise la fonction ci-dessous.

vector<string> split(const string& i_str, const string& i_delim)
{
    vector<string> result;

    size_t found = i_str.find(i_delim);
    size_t startIndex = 0;

    while(found != string::npos)
    {
        result.push_back(string(i_str.begin()+startIndex, i_str.begin()+found));
        startIndex = found + i_delim.size();
        found = i_str.find(i_delim, startIndex);
    }
    if(startIndex != i_str.size())
        result.push_back(string(i_str.begin()+startIndex, i_str.end()));
    return result;      
}

1 votes

Je l'ai testé, et ça marche. Merci ! À mon avis, c'est la meilleure réponse car, comme l'indique le répondant original, cette solution réduit l'encombrement de la mémoire et le résultat est commodément stocké dans un vecteur. (réplique de la méthode Python string.split() méthode.)

6voto

Benjamin Lindley Points 51005

J'utiliserais boost::tokenizer . Voici de la documentation expliquant comment créer une fonction tokenizer appropriée : http://www.boost.org/doc/libs/1_52_0/libs/tokenizer/tokenizerfunction.htm

En voici une qui convient à votre cas.

struct my_tokenizer_func
{
    template<typename It>
    bool operator()(It& next, It end, std::string & tok)
    {
        if (next == end)
            return false;
        char const * del = ">=";
        auto pos = std::search(next, end, del, del + 2);
        tok.assign(next, pos);
        next = pos;
        if (next != end)
            std::advance(next, 2);
        return true;
    }

    void reset() {}
};

int main()
{
    std::string to_be_parsed = "1) one>=2) two>=3) three>=4) four";
    for (auto i : boost::tokenizer<my_tokenizer_func>(to_be_parsed))
        std::cout << i << '\n';
}

3 votes

Merci. Mais je ne veux souhaiter que du C++ standard et non une bibliothèque tierce.

0 votes

@TheCrazyProgrammer : Ok, quand j'ai lu "Standard C++", j'ai pensé que cela signifiait pas d'extensions non standard, pas que vous ne pouviez pas utiliser des bibliothèques tierces conformes aux standards.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X