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.

864voto

Vincenzo Pii Points 4137

Vous pouvez utiliser le std::string::find() pour trouver la position de votre délimiteur de chaîne, puis utilisez la fonction 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"
  • Le site find(const string& str, size_t pos = 0) renvoie la position de la première occurrence de str dans la chaîne, ou npos si la chaîne n'est pas trouvée.

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


Si vous avez des délimiteurs multiples, après avoir extrait un token, vous pouvez le supprimer (délimiteur inclus) pour procéder aux extractions suivantes (si vous voulez préserver la chaîne originale, utilisez simplement s = s.substr(pos + delimiter.length()); ) :

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

De cette façon, vous pouvez facilement boucler 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

125 votes

Pour ceux qui ne veulent pas modifier la chaîne de caractères d'entrée, faire size_t last = 0; size_t next = 0; while ((next = s.find(delimiter, last)) != string::npos) { cout << s.substr(last, next-last) << endl; last = next + 1; } cout << s.substr(last) << endl;

52 votes

NOTE : mushroom les sorties en dehors de la boucle, c'est-à-dire s = mushroom

2 votes

Ces échantillons n'extraient pas le dernier jeton de la chaîne. Un exemple de mon extraction d'un IpV4 d'une chaîne de caractères : <code>size_t last = 0 ; size_t next = 0 ; int index = 0 ; while (index<4) { next = str.find(delimiter, last) ; auto number = str.substr(last, next - last) ; IPv4[index++] = atoi(number.c_str()) ; last = next + 1 ; }</code>

118voto

moswald Points 4521

Cette méthode utilise std::string::find sans muter la chaîne originale en se souvenant du début et de la fin du précédent jeton de sous-chaîne.

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

0 votes

Comment puis-je effectuer cette opération sur un vecteur<string> où les deux chaînes du vecteur sont de même forme et ont les mêmes délimiteurs. Je veux juste sortir les deux chaînes analysées de la même manière que cela fonctionne pour une seule chaîne. Mon "délimiteur de chaîne" restera le même, bien sûr.

104voto

Arafat Hasan Points 411

Pour le délimiteur de chaîne

Divise la chaîne de caractères en fonction d'un délimiteur de chaîne . Comme la division d'une chaîne de caractères "adsf-+qwret-+nvfkbdsj-+orthdfjgh-+dfjrleih" basé sur le délimiteur de chaîne "-+" le résultat 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 le délimiteur à un seul caractère

Divise la chaîne de caractères en fonction d'un délimiteur de caractères. Par exemple, diviser une chaîne de caractères "adsf+qwer+poui+fdgh" avec le délimiteur "+" produira {"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

0 votes

Vous revenez vector<string> Je pense que ça va appeler le constructeur de copie.

6 votes

Toutes les références que j'ai vues montrent que l'appel au constructeur de copie est éliminé dans ce contexte.

0 votes

Avec les compilateurs "modernes" (C++03 ?), je pense que c'est correct, la sémantique RVO et/ou move éliminera le constructeur de copie.

60voto

Sviatoslav Points 687

Vous pouvez utiliser la fonction next pour diviser une chaîne de caractères :

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

10 votes

IMO il ne fonctionne pas comme prévu : split("abc","a") retournera un vecteur ou une seule chaîne de caractères, "bc" alors que je pense qu'il serait plus logique de renvoyer un vecteur d'éléments. ["", "bc"] . Utilisation de str.split() en Python, il était intuitif pour moi qu'il devait retourner une chaîne vide dans le cas où delim a été trouvé soit au début, soit à la fin, mais ce n'est que mon opinion. Quoi qu'il en soit, je pense juste que cela devrait être mentionné

3 votes

Je recommande vivement de supprimer le if (!token.empty()) éviter le problème mentionné par @kyriakosSt ainsi que d'autres problèmes liés aux délimiteurs consécutifs.

1 votes

Je retirerais ma note positive si je le pouvais, mais SO ne me le permet pas. La question soulevée par @kyriakosSt est un problème, et le fait d'enlever la if (!token.empty()) ne semble pas suffire à le réparer.

35voto

Nox Points 73

Une façon de le faire avec C++20 :

#include <iostream>
#include <ranges>
#include <string_view>

int main()
{
    std::string hello = "text to be parsed";
    auto split = hello
        | std::ranges::views::split(' ')
        | std::ranges::views::transform([](auto&& str) { return std::string_view(&*str.begin(), std::ranges::distance(str)); });

    for (auto&& word : split)
    {
        std::cout << word << std::endl;
    }
}

Voir : https://stackoverflow.com/a/48403210/10771848 https://en.cppreference.com/w/cpp/ranges/split_view

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