web-dev-qa-db-fra.com

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

J'analyse une chaîne en C++ en utilisant les éléments suivants:

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

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

L'analyse avec un seul délimiteur de caractères convient. Mais si je veux utiliser une chaîne comme délimiteur.

Exemple: je veux diviser:

scott>=tiger

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

228
TheCrazyProgrammer

Vous pouvez utiliser la fonction std::string::find() pour rechercher la position de votre délimiteur de chaîne, puis utiliser std::string::substr() pour obtenir un jeton.

Exemple:

std::string s = "scott>=tiger";
std::string delimiter = ">=";
std::string token = s.substr(0, s.find(delimiter)); // token is "scott"
  • La fonction find(const string& str, size_t pos = 0) renvoie la position de la première occurrence de str dans la chaîne, ou de npos si la chaîne n'est pas trouvée.

  • La fonction substr(size_t pos = 0, size_t n = npos) renvoie une sous-chaîne de l'objet, commençant à la position pos et de longueur npos.


Si vous avez plusieurs délimiteurs, après avoir extrait un jeton, vous pouvez le supprimer (délimiteur inclus) pour procéder aux extractions suivantes (si vous souhaitez conserver la chaîne d'origine, utilisez simplement s = s.substr(pos + delimiter.length());):

s.erase(0, s.find(delimiter) + delimiter.length());

De cette façon, vous pouvez facilement faire une boucle pour obtenir chaque jeton.

Exemple complet 

std::string s = "scott>=tiger>=mushroom";
std::string delimiter = ">=";

size_t pos = 0;
std::string token;
while ((pos = s.find(delimiter)) != std::string::npos) {
    token = s.substr(0, pos);
    std::cout << token << std::endl;
    s.erase(0, pos + delimiter.length());
}
std::cout << s << std::endl;

Sortie:

scott
tiger
mushroom
412
Vincenzo Pii

Cette méthode utilise std::string::find sans transformer la chaîne d'origine en mémorisant le début et la fin du jeton de sous-chaîne précédent.

#include <iostream>
#include <string>

int main()
{
    std::string s = "scott>=tiger";
    std::string delim = ">=";

    auto start = 0U;
    auto end = s.find(delim);
    while (end != std::string::npos)
    {
        std::cout << s.substr(start, end - start) << std::endl;
        start = end + delim.length();
        end = s.find(delim, start);
    }

    std::cout << s.substr(start, end);
}
42
moswald

Vous pouvez utiliser la fonction suivante pour scinder la chaîne:

vector<string> split(const string& str, const string& delim)
{
    vector<string> tokens;
    size_t prev = 0, pos = 0;
    do
    {
        pos = str.find(delim, prev);
        if (pos == string::npos) pos = str.length();
        string token = str.substr(prev, pos-prev);
        if (!token.empty()) tokens.Push_back(token);
        prev = pos + delim.length();
    }
    while (pos < str.length() && prev < str.length());
    return tokens;
}
26
Sviatoslav

strtok vous permet de transmettre plusieurs caractères en tant que délimiteurs. Je parie que si vous passiez "> =" votre exemple de chaîne serait fractionné correctement (même si les> et = sont comptés comme des séparateurs individuels).

EDIT si vous ne voulez pas utiliser c_str() pour convertir de chaîne en char *, vous pouvez utiliser substr et find_first_of pour 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());
}
13
ryanbwork

Pour le délimiteur de chaîne

Fractionner une chaîne en fonction d'un délimiteur string. Telle que fractionner la chaîne "adsf-+qwret-+nvfkbdsj-+orthdfjgh-+dfjrleih" en fonction du délimiteur de chaîne "-+", la sortie sera {"adsf", "qwret", "nvfkbdsj", "orthdfjgh", "dfjrleih"}

#include <iostream>
#include <sstream>
#include <vector>

using namespace std;

// for string delimiter
vector<string> split (string s, string delimiter) {
    size_t pos_start = 0, pos_end, delim_len = delimiter.length();
    string token;
    vector<string> res;

    while ((pos_end = s.find (delimiter, pos_start)) != string::npos) {
        token = s.substr (pos_start, pos_end - pos_start);
        pos_start = pos_end + delim_len;
        res.Push_back (token);
    }

    res.Push_back (s.substr (pos_start));
    return res;
}

int main() {
    string str = "adsf-+qwret-+nvfkbdsj-+orthdfjgh-+dfjrleih";
    string delimiter = "-+";
    vector<string> v = split (str, delimiter);

    for (auto i : v) cout << i << endl;

    return 0;
}


Sortie

 adsf 
 qwret 
 nvfkbdsj 
 orthdfjgh 
 dfjrleih 




Pour un caractère unique

Fractionner la chaîne en fonction d'un séparateur de caractères. Tels que la division de la chaîne "adsf+qwer+poui+fdgh" avec le délimiteur "+" générera {"adsf", "qwer", "poui", "fdg"h}

#include <iostream>
#include <sstream>
#include <vector>

using namespace std;

vector<string> split (const string &s, char delim) {
    vector<string> result;
    stringstream ss (s);
    string item;

    while (getline (ss, item, delim)) {
        result.Push_back (item);
    }

    return result;
}

int main() {
    string str = "adsf+qwer+poui+fdgh";
    vector<string> v = split (str, '+');

    for (auto i : v) cout << i << endl;

    return 0;
}


Sortie

 adsf 
 qwer 
 poui 
 fdgh 
9
Arafat Hasan

Ce code sépare les lignes du texte et ajoute tout le monde 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");
9
William Cuervo

Je voudrais utiliser boost::tokenizer. Voici 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 un qui fonctionne pour 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';
}
4
Benjamin Lindley

Voici mon point de vue sur ceci. Il gère les cas Edge et utilise un paramètre facultatif pour supprimer les entrées vides des résultats.

bool endsWith(const std::string& s, const std::string& suffix)
{
    return s.size() >= suffix.size() &&
           s.substr(s.size() - suffix.size()) == suffix;
}

std::vector<std::string> split(const std::string& s, const std::string& delimiter, const bool& removeEmptyEntries = false)
{
    std::vector<std::string> tokens;

    for (size_t start = 0, end; start < s.length(); start = end + delimiter.length())
    {
         size_t position = s.find(delimiter, start);
         end = position != string::npos ? position : s.length();

         std::string token = s.substr(start, end - start);
         if (!removeEmptyEntries || !token.empty())
         {
             tokens.Push_back(token);
         }
    }

    if (!removeEmptyEntries &&
        (s.empty() || endsWith(s, delimiter)))
    {
        tokens.Push_back("");
    }

    return tokens;
}

Exemples

split("a-b-c", "-"); // [3]("a","b","c")

split("a--c", "-"); // [3]("a","","c")

split("-b-", "-"); // [3]("","b","")

split("--c--", "-"); // [5]("","","c","","")

split("--c--", "-", true); // [1]("c")

split("a", "-"); // [1]("a")

split("", "-"); // [1]("")

split("", "-", true); // [0]()
3

Il s'agit d'une méthode complète qui divise la chaîne sur un séparateur et renvoie un vecteur des chaînes coupées.

C'est une adaptation de la réponse de ryanbwork. Cependant, sa 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. Ceci est ma solution à ce problème.

vector<string> Split(string mystring, string delimiter)
{
    vector<string> subStringList;
    string token;
    while (true)
    {
        size_t findfirst = mystring.find_first_of(delimiter);
        if (findfirst == string::npos) //find_first_of returns npos if it couldn't find the delimiter anymore
        {
            subStringList.Push_back(mystring); //Push back the final piece of mystring
            return subStringList;
        }
        token = mystring.substr(0, mystring.find_first_of(delimiter));
        mystring = mystring.substr(mystring.find_first_of(delimiter) + 1);
        subStringList.Push_back(token);
    }
    return subStringList;
}
2
Amber Elferink

Si vous ne souhaitez pas modifier la chaîne (comme dans la réponse de Vincenzo Pii) et si vous souhaitez également afficher le dernier jeton, vous pouvez utiliser cette approche:

inline std::vector<std::string> splitString( const std::string &s, const std::string &delimiter ){
    std::vector<std::string> ret;
    size_t start = 0;
    size_t end = 0;
    size_t len = 0;
    std::string token;
    do{ end = s.find(delimiter,start); 
        len = end - start;
        token = s.substr(start, len);
        ret.emplace_back( token );
        start += len + delimiter.length();
        std::cout << token << std::endl;
    }while ( end != std::string::npos );
    return ret;
}
1
user2366975
#include<iostream>
#include<algorithm>
using namespace std;

int split_count(string str,char delimit){
return count(str.begin(),str.end(),delimit);
}

void split(string str,char delimit,string res[]){
int a=0,i=0;
while(a<str.size()){
res[i]=str.substr(a,str.find(delimit));
a+=res[i].size()+1;
i++;
}
}

int main(){

string a="abc.xyz.mno.def";
int x=split_count(a,'.')+1;
string res[x];
split(a,'.',res);

for(int i=0;i<x;i++)
cout<<res[i]<<endl;
  return 0;
}

P.S: Fonctionne uniquement si les longueurs des chaînes après division sont égales

0
Sudhakar R

La réponse est déjà là, mais la réponse sélectionnée utilise la fonction d'effacement, ce qui est très coûteux, pensez à une très grosse chaîne (en Mo). Par conséquent, 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)
    {
        string temp(i_str.begin()+startIndex, i_str.begin()+found);
        result.Push_back(temp);
        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;      
}
0
Shubham Agrawal