web-dev-qa-db-fra.com

C++ - Scinder une chaîne par regex

Je veux diviser std::string par regex.

J'ai trouvé des solutions sur Stackoverflow, mais la plupart d'entre elles divisent des chaînes en un seul espace ou utilisent des bibliothèques externes telles que boost.

Je ne peux pas utiliser de boost.

Je veux diviser la chaîne par regex - "\\s+".

J'utilise cette version g ++ g++ (Debian 4.4.5-8) 4.4.5 et je ne peux pas effectuer de mise à niveau.

13

Vous n'avez pas besoin d'utiliser des expressions régulières si vous voulez simplement scinder une chaîne de caractères en plusieurs espaces. Écrire votre propre bibliothèque de regex est exagéré pour quelque chose d'aussi simple.

La réponse que vous avez liée dans vos commentaires, Fractionner une chaîne en C++? , peut facilement être modifié pour ne pas inclure d'éléments vides s'il y a plusieurs espaces. 

std::vector<std::string> &split(const std::string &s, char delim,std::vector<std::string> &elems) {
    std::stringstream ss(s);
    std::string item;
    while (std::getline(ss, item, delim)) {
        if (item.length() > 0) {
            elems.Push_back(item);  
        }
    }
    return elems;
}


std::vector<std::string> split(const std::string &s, char delim) {
    std::vector<std::string> elems;
    split(s, delim, elems);
    return elems;
}

En vérifiant que item.length() > 0 avant d’appuyer item sur le vecteur elems, vous n’obtiendrez plus d’éléments supplémentaires si votre entrée contient plusieurs délimiteurs (espaces dans votre cas).

8
shf301
std::regex rgx("\\s+");
std::sregex_token_iterator iter(string_to_split.begin(),
    string_to_split.end(),
    rgx,
    -1);
std::sregex_token_iterator end;
for ( ; iter != end; ++iter)
    std::cout << *iter << '\n';

Le -1 est la clé ici: lorsque l'itérateur est construit, il pointe sur le texte qui précède la correspondance et après chaque incrément, il pointe sur le texte qui a suivi la correspondance précédente.

Si vous n'avez pas C++ 11, la même chose devrait fonctionner avec TR1 ou (éventuellement avec une légère modification) avec Boost.

38
Pete Becker

Pour développer la réponse de @Pete Becker, je donne un exemple de fonction resplit qui peut être utilisée pour fractionner du texte à l'aide de regexp:

  std::vector<std::string>
  resplit(const std::string & s, std::string rgx_str = "\\s+") {


      std::vector<std::string> elems;

      std::regex rgx (rgx_str);

      std::sregex_token_iterator iter(s.begin(), s.end(), rgx, -1);
      std::sregex_token_iterator end;

      while (iter != end)  {
          //std::cout << "S43:" << *iter << std::endl;
          elems.Push_back(*iter);
          ++iter;
      }

      return elems;

  }

Cela fonctionne comme suit:

   string s1 = "first   second third    ";
   vector<string> v22 = my::resplit(s1);

   for (const auto & e: v22) {
       cout <<"Token:" << e << endl;
   }


   //Token:first
   //Token:second
   //Token:third


   string s222 = "first|second:third,forth";
   vector<string> v222 = my::resplit(s222, "[|:,]");

   for (const auto & e: v222) {
       cout <<"Token:" << e << endl;
   }


   //Token:first
   //Token:second
   //Token:third
   //Token:forth
7
Marcin
string s = "foo bar  baz";
regex e("\\s+");
regex_token_iterator<string::iterator> i(s.begin(), s.end(), e, -1);
regex_token_iterator<string::iterator> end;
while (i != end)
   cout << " [" << *i++ << "]";

imprime [foo] [bar] [baz]

1
solstice333