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.

6voto

Voici ce que j'en pense. Elle gère les cas limites et prend un paramètre optionnel 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]()

6voto

hmofrad Points 871

Cela devrait fonctionner parfaitement pour les délimiteurs de chaîne (ou de caractère unique). N'oubliez pas d'inclure #include <sstream> .

std::string input = "Alfa=,+Bravo=,+Charlie=,+Delta";
std::string delimiter = "=,+"; 
std::istringstream ss(input);
std::string token;
std::string::iterator it;

while(std::getline(ss, token, *(it = delimiter.begin()))) {
    std::cout << token << std::endl; // Token is extracted using '='
    it++;
    // Skip the rest of delimiter if exists ",+"
    while(it != delimiter.end() and ss.peek() == *(it)) { 
        it++; ss.get(); 
    }
}

La première boucle while extrait un jeton en utilisant le premier caractère du délimiteur de chaîne. La deuxième boucle while saute le reste du délimiteur et s'arrête au début du token suivant.

0 votes

Ceci est incorrect. Si l'entrée est modifiée comme ci-dessous, elle sera divisée en utilisant le premier =, alors qu'elle n'est pas censée le faire : std::string input = "Alfa=,+Bravo=,+Charlie=,+Delta=Echo";

0 votes

@Amitoj Bien vu. J'ai révisé ma réponse pour couvrir également les entrées avec des délimiteurs mal formés.

5voto

Une approche très simple/naïve :

vector<string> words_seperate(string s){
    vector<string> ans;
    string w="";
    for(auto i:s){
        if(i==' '){
           ans.push_back(w);
           w="";
        }
        else{
           w+=i;
        }
    }
    ans.push_back(w);
    return ans;
}

Vous pouvez également utiliser la fonction de division de la bibliothèque Boost :

vector<string> result; 
boost::split(result, input, boost::is_any_of("\t"));

Vous pouvez aussi essayer TOKEN ou strtok :

char str[] = "DELIMIT-ME-C++"; 
char *token = strtok(str, "-"); 
while (token) 
{ 
    cout<<token; 
    token = strtok(NULL, "-"); 
} 

Ou vous pouvez faire ça :

char split_with=' ';
vector<string> words;
string token; 
stringstream ss(our_string);
while(getline(ss , token , split_with)) words.push_back(token);

4voto

Amber Elferink Points 50

Il s'agit d'une méthode complète qui divise la chaîne de caractères sur n'importe quel délimiteur et renvoie un vecteur des chaînes de caractères découpées.

C'est une adaptation de la réponse de ryanbwork. Cependant, son chèque pour : if(token != mystring) donne des résultats erronés si vous avez des éléments répétitifs dans votre chaîne. Voici 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 votes

Quelque chose comme while (true) est généralement effrayant à voir dans un morceau de code comme celui-ci. Personnellement, je recommanderais de réécrire ce code de façon à ce que la comparaison à std::string::npos (ou respectivement un contrôle contre mystring.size() ) fait de la while (true) obsolète.

3voto

cdahms Points 572

Puisqu'il s'agit du meilleur résultat de recherche Stack Overflow Google pour C++ split string ou similaire, je posterai un exemple complet, copié/collé et exécutable qui montre les deux méthodes.

splitString utilise stringstream (probablement la meilleure option et la plus facile dans la plupart des cas)

splitString2 utilise find et substr (une approche plus manuelle)

// SplitString.cpp

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

// function prototypes
std::vector<std::string> splitString(const std::string& str, char delim);
std::vector<std::string> splitString2(const std::string& str, char delim);
std::string getSubstring(const std::string& str, int leftIdx, int rightIdx);

int main(void)
{
  // Test cases - all will pass

  std::string str = "ab,cd,ef";
  //std::string str = "abcdef";
  //std::string str = "";
  //std::string str = ",cd,ef";
  //std::string str = "ab,cd,";   // behavior of splitString and splitString2 is different for this final case only, if this case matters to you choose which one you need as applicable

  std::vector<std::string> tokens = splitString(str, ',');

  std::cout << "tokens: " << "\n";

  if (tokens.empty())
  {
    std::cout << "(tokens is empty)" << "\n";
  }
  else
  {
    for (auto& token : tokens)
    {
      if (token == "") std::cout << "(empty string)" << "\n";
      else std::cout << token << "\n";
    }
  }

  return 0;
}

std::vector<std::string> splitString(const std::string& str, char delim)
{
  std::vector<std::string> tokens;

  if (str == "") return tokens;

  std::string currentToken;

  std::stringstream ss(str);

  while (std::getline(ss, currentToken, delim))
  {
    tokens.push_back(currentToken);
  }

  return tokens;
}

std::vector<std::string> splitString2(const std::string& str, char delim)
{
  std::vector<std::string> tokens;

  if (str == "") return tokens;

  int leftIdx = 0;

  int delimIdx = str.find(delim);

  int rightIdx;

  while (delimIdx != std::string::npos)
  {
    rightIdx = delimIdx - 1;

    std::string token = getSubstring(str, leftIdx, rightIdx);
    tokens.push_back(token);

    // prep for next time around
    leftIdx = delimIdx + 1;

    delimIdx = str.find(delim, delimIdx + 1);
  }

  rightIdx = str.size() - 1;

  std::string token = getSubstring(str, leftIdx, rightIdx);
  tokens.push_back(token);

  return tokens;
}

std::string getSubstring(const std::string& str, int leftIdx, int rightIdx)
{
  return str.substr(leftIdx, rightIdx - leftIdx + 1);
}

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