std::regex_replace

std::regex_replace - C++ - Programmation

Marsh Posté le 05-03-2022 à 15:01:33    

Hello à tous,
Y'a t'il parmi vous des expert de la regex ? Malgré toute ma bonne volonté, mon cerveau segfault sur ces trucs.
 
Voici ce que je veux faire :  
 

Code :
  1. int main()
  2. {
  3. std::string pattern = "http://www.server.com/{z}/{x}/{y}.png";
  4. std::regex reg("some magick here" );
  5. int x = 42;
  6. int y = 69;
  7. int z = 13;
  8. std::string str = std::regex_replace(pattern, reg, "and here" );
  9. std::cout << str << '\n';
  10. return 0;
  11. }


 
Je veux remplacer "{x}" par le contenu de la variable x, "{y}" par le contenu de la variable y et "{z}" par le contenu de la variable z.
 
Si vous voyez une autre solution, je suis preneur.
 
PS : l'ordre de {x}, {y} et {z} dans la variable pattern peuvent changer (donc exit printf).
 
Je vous remercie grandement pour votre aide.

Reply

Marsh Posté le 05-03-2022 à 15:01:33   

Reply

Marsh Posté le 05-03-2022 à 18:32:16    

Ce code le fait :

Code :
  1. #include <algorithm>
  2. #include <iostream>
  3. #include <list>
  4. #include <string_view>
  5. #include <vector>
  6. class AdHocStuff
  7. {
  8. public:
  9.     AdHocStuff(std::size_t pos, int val) : m_pos(pos), m_val(val){}
  10.     auto pos()  const {return m_pos;}
  11.     auto val()  const {return m_val;}
  12.     auto val(int val) {this->m_val = val;}
  13. private:
  14.     std::size_t m_pos;
  15.     int m_val;
  16. };
  17. int main()
  18. {
  19.     // Données initiales
  20.     std::string_view sv = std::string_view("http://www.server.com/{z}/{x}/{y}.png" );
  21.     std::list<std::string> vars = {"x", "y", "z"};
  22.     std::list<std::string>::iterator it1;
  23.     std::list<int> values = {42, 69, 13};
  24.     std::list<int>::iterator it2;
  25.     // Construction des données position/valeur
  26.     std::vector<AdHocStuff> stuff;
  27.     for(it1 = vars.begin(), it2 = values.begin(); it1 != vars.end() && it2 != values.end(); ++it1, ++it2) {
  28.         std::string s;
  29.         stuff.push_back(AdHocStuff(sv.find(s.append("{" ).append(*it1).append("}" )), *it2));
  30.     }
  31.     // On les trie par position
  32.     std::ranges::sort(stuff, {}, &AdHocStuff::pos);
  33.     // On découpe la chaine en sortie maintenant qu'on a les positions a remplacer
  34.     std::size_t current_pos = 0;
  35.     for (auto const& s : stuff) {
  36.         std::cout << sv.substr(current_pos, s.pos() - current_pos) << s.val();
  37.         current_pos = s.pos() + 3;
  38.     }
  39.     std::cout << sv.substr(current_pos) << '\n';
  40. }


 g++ -std=c++20 -o adhocstuff.exe adhocstuff.cpp
C'est du C++ 20 à cause de la ligne avec std::ranges::sort
 
Bon, j'ai pas été voir les conditions aux limites, c'était juste pour me faire la main avec un petit exercice, ça fait pas loin de 10 ans que j'avais rien codé dans ce langage. Et pour être propre, on pourrait stocker le nom de ce qui est a remplacer a chaque fois, et avancer de sa taille, au lieu d'avancer de 3 a chaque étape :
 

Code :
  1. #include <algorithm>
  2. #include <stdexcept>
  3. #include <iostream>
  4. #include <list>
  5. #include <string_view>
  6. #include <vector>
  7. class AdHocStuff
  8. {
  9. public:
  10.   AdHocStuff(std::size_t pos, int val, std::string const& var) : m_pos(pos), m_val(val), m_var(var){}
  11.   auto pos()  const {return m_pos;}
  12.   auto val()  const {return m_val;}
  13.   auto val(int val) {this->m_val = val;}
  14.   auto var()  const {return m_var;}
  15.   // auto print() const {std::cout << "pos : " << m_pos << " val : " << m_val << " var : " << m_var << std::endl;}
  16. private:
  17.   std::size_t m_pos;
  18.   int m_val;
  19.   std::string m_var;
  20. };
  21. int main()
  22. {
  23.   // Données initiales
  24.   std::string_view sv = std::string_view("http://www.server.com/{z}/{x}/{y}.png" );
  25.   std::list<std::string> vars = {"x", "y", "z"};
  26.   std::list<int> values = {42, 69, 13};
  27.  
  28.   try {
  29.     // Vérification de la cohérence
  30.     if (values.size() < vars.size()) {
  31.       throw std::invalid_argument("There are less values than variables." );
  32.     }
  33.     // Construction des données position/valeur
  34.     std::vector<AdHocStuff> stuff;
  35.     std::list<std::string>::iterator it1;
  36.     std::list<int>::iterator it2;
  37.     std::size_t const max_pos = sv.size();
  38.     for(it1 = vars.begin(), it2 = values.begin(); it1 != vars.end() && it2 != values.end(); ++it1, ++it2) {
  39.       std::string temp;
  40.       stuff.push_back(AdHocStuff(std::min(sv.find(temp.append("{" ).append(*it1).append("}" )), max_pos), *it2, temp)); // effet de bord pour le 2e temp
  41.     }
  42.     // On les trie par position
  43.     std::ranges::sort(stuff, {}, &AdHocStuff::pos);
  44.     // On découpe la chaine en sortie maintenant qu'on a les positions a remplacer
  45.     std::size_t current_pos = 0;
  46.     for (auto const& s : stuff) {
  47.       if (s.pos() < max_pos ) {
  48.         std::cout << sv.substr(current_pos, s.pos() - current_pos) << s.val();
  49.         current_pos = s.pos() + s.var().size();
  50.       }
  51.     }
  52.     if (current_pos < max_pos) {
  53.       std::cout << sv.substr(current_pos, max_pos);
  54.     }
  55.     std::cout << std::flush;
  56.   }
  57.   catch (std::invalid_argument& e) {
  58.     std::cerr << e.what() << std::endl;
  59.     return -1;
  60.   }
  61.   catch (...) {
  62.     std::cerr << "An unexpected error occurred." << std::endl;
  63.     return -1;
  64.   }
  65.   return 0;
  66. }


 
Cette dernière version est un peu plus robuste, supporte des variables manquantes et des variables avec des noms complexes.
Par contre, ce code ne saura pas remplacer plusieurs occurrences d'une même variable, il faudrait modifier un peu les structures de données pour cela.
 
A+,


Message édité par gilou le 05-03-2022 à 20:26:45

---------------
There's more than what can be linked! --    Iyashikei Anime Forever!    --  AngularJS c'est un framework d'engulé!  --
Reply

Marsh Posté le 05-03-2022 à 20:32:13    

Hello.
Merci pour ton code.
 
Je m'en suis finalement sorti comme ca :  

Code :
  1. int main()
  2. {
  3. std::string pattern = "http://www.server.com/{z}/{x}/{y}.png";
  4. int x = 42;
  5. int y = 69;
  6. int z = 13;
  7. pattern = std::regex_replace(pattern, std::regex(R"(\{x\})" ), std::to_string(x));
  8. pattern = std::regex_replace(pattern, std::regex(R"(\{y\})" ), std::to_string(y));
  9. pattern = std::regex_replace(pattern, std::regex(R"(\{z\})" ), std::to_string(z));
  10. std::cout << '\n' << pattern << '\n';
  11. return 0;
  12. }

Reply

Marsh Posté le 06-03-2022 à 10:31:18    

Question bête : si z x et y valent toujours z x et y pourquoi utiliser une regex plutôt qu'une simple substitution de chaîne ?


---------------
Topic .Net - C# @ Prog
Reply

Marsh Posté le 06-03-2022 à 11:53:43    

Justement car l'ordre peut changer.

Reply

Marsh Posté le 06-03-2022 à 13:02:20    

L'ordre non plus n'a aucune importance si ça reste x y et z...


---------------
Topic .Net - C# @ Prog
Reply

Marsh Posté le 06-03-2022 à 14:45:49    

Comment tu fais ca ?!

Reply

Marsh Posté le 06-03-2022 à 16:18:54    

Je laisse les gens qui causent le C++ donner la bonne réponse, mais j'imagine que ça donne un truc du genre

 

pattern.replace(pattern.find("{x}" ),3,"42" );

 

(ou la même chose avec une boucle pour choper x y et z).
?

 


Message édité par TotalRecall le 06-03-2022 à 16:19:37

---------------
Topic .Net - C# @ Prog
Reply

Marsh Posté le 06-03-2022 à 16:40:19    

Je connais pas c++ mais dans la plupart des langages que je connais les regex sont nettement moins performant que des "rechercher / remplacer".
Par exemple en php on ferait:

Code :
  1. $string= str_replace(array('{x}','{y}','{z}',), array($x, $y, $z), $pattern);


Je supposes qu'il existe l'équivalent en c++...
 
https://www.google.com/search?q=c%2 [...] erformance
 
Après ce billet semble dire que Regex.Replace est le plus performant.


---------------
D3
Reply

Marsh Posté le 06-03-2022 à 21:48:26    

Oui, au final, pour ce qu'il veut faire, std::regex_replace est plus adapté, parce que il va pouvoir tenir compte du cas de plusieurs fois un {x} dans la chaine de départ.
Je vais adapter mon code pour l'utiliser :  

Code :
  1. #include <algorithm>
  2. #include <stdexcept>
  3. #include <iostream>
  4. #include <list>
  5. #include <regex>
  6. int main()
  7. {
  8.     // Données initiales
  9.    // a gérer avec du boost pour récupérer cela en ligne de commande
  10.     std::string s = std::string("http://www.server.com/{z}/{x}/{y}.png" );
  11.     std::list<std::string> vars = {"x", "y", "z"};
  12.     std::list<int> values = {42, 69, 13};
  13.  
  14.   try {
  15.     // Vérification de la cohérence
  16.     // pas de noms de variables dupliquées
  17.     std::list<std::string> ovars;
  18.     std::copy(vars.begin(), vars.end(), std::back_inserter(ovars));
  19.     ovars.sort();
  20.     if (std::adjacent_find(ovars.begin(), ovars.end()) != ovars.end()) {
  21.       throw std::invalid_argument("There are duplicated variables." );
  22.     }
  23.     // au moins autant de valeurs que de variables
  24.     if (values.size() < vars.size()) {
  25.       throw std::invalid_argument("There are less values than variables." );
  26.     }
  27.     // on parcourt les deux listes en parallèles, et on fait un regex_replace pour la variable à chaque étape
  28.     std::list<int>::iterator it1;
  29.     std::list<std::string>::iterator it2;
  30.     for(it1 = values.begin(), it2 = vars.begin(); it1 != values.end() && it2 != vars.end(); ++it1, ++it2) {
  31.       std::string temp;
  32.       s = std::regex_replace(s, std::regex(temp.append("\\{" ).append(*it2).append("\\}" )), std::to_string(*it1));
  33.     }
  34.     std::cout << s << std::flush;
  35.   }
  36.   catch (std::invalid_argument& e) {
  37.     std::cerr << "Bad data : " << e.what() << std::endl;
  38.     return -1;
  39.   }
  40.   catch (...) {
  41.     std::cerr << "An unexpected error occurred." << std::endl;
  42.     return -1;
  43.   }
  44.   return 0;
  45. }


Bref l'algo lui même se réduit à  

Code :
  1. // Données en entrée
  2. std::string s = std::string("http://www.server.com/{z}/{x}/{y}.png" );
  3. std::list<std::string> vars = {"x", "y", "z"};
  4. std::list<int> values = {42, 69, 13};
  5. // Algo de remplacement au moyen d'une application de regex_replace
  6. std::list<std::string>::iterator it1;
  7. std::list<int>::iterator it2;
  8. for(it1 = vars.begin(), it2 = values.begin(); it1 != vars.end() && it2 != values.end(); ++it1, ++it2) {
  9.   std::string temp;
  10.   s = std::regex_replace(s, std::regex(temp.append("\\{" ).append(*it1).append("\\}" )), std::to_string(*it2));
  11. }
  12. // Résultat en sortie
  13. std::cout << s << std::flush;


Comme je l'ai dit, ça fait des années que j'ai pas fait de c++, je suppose qu'il y a des trucs plus efficaces que de copier une liste, la trier, et chercher deux éléments consécutifs identiques, comme j'ai fait à la bourrin, pour voir si la liste initiale contient des doublons.
Il y aurait bien ceci :  

Code :
  1. std::set<std::string> svars(vars.begin(), vars.end());
  2. if (svars.size() != vars.size()) {
  3.   throw std::invalid_argument("There are duplicated variables." );
  4. }

mais je subodore que c'est équivalent a ce que je fais voire un poil plus couteux.
 
 
A+,


Message édité par gilou le 07-03-2022 à 03:51:42

---------------
There's more than what can be linked! --    Iyashikei Anime Forever!    --  AngularJS c'est un framework d'engulé!  --
Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed