02/09/2015
Paradoxalement c'est en C++ que les expressions régulières sont les plus simples à employer. Il suffit de connaître la syntaxe ECMAScript et les trois méthodes regex_match
, regex_search
et regex_replace
. Accessoirement nous utiliserons une nouveauté de C++11 : for x: conteneur
Le site cplusplus.com est certainement le plus clair et le plus documenté.
C'est la syntaxe utilisé par défaut par C++. La syntaxe rappelle celle bash.
Les caractères spéciaux sont:
^
: Début de la ligne ou de la chaîne $
: Fin de ligne ou de chaîne \
: caractère d'échappement des caractères spéciaux .
: n'importe quel caractère *
: 0 ou plusieurs fois +
: 1 fois ou plus ?
: 0 ou une fois ( )
: groupe, on y fait appel avec $1, $2 ou m[1], m[2] etc. [ ]
: ensemble |
: alternative Exemples :
$1
.# commentaire
"+ Ligne n° 1"
.lien
. zob
est inconvenant, mais convient quand même.On souhaite reconnaitre une url http ou ftp.
L'expression régulière est "(http|ftp)://([^. ]+)\\.([^. ]+)\\.(.+)"
Elle se décompose ainsi :
(ftp|http)
: groupe, concorde avec 'http' ou 'ftp'
://
: tel quel, ne constitue pas un groupe
([^. ]+)
: groupe, correspond à un ou plusieurs caractères ni point ni espace. Remarque : entre [] il n'est pas nécessaire d'échapper le point
\\.
: un point, ne constitue pas un groupe
([^. ]+)
: déjà rencontrée. Dans [^. ]
le circonflexe signifie non et pas début.
\\.
: encore un point
(.+)
: groupe, le reste de l'expression
Finalement la regex est "(http|ftp)://([^.]+)\\.([^.]+)\\.(.+)"
La syntaxe simple est regex_match(s, re)
mais on emploie plus souvent la syntaxe regex(s, m, re)
où s est la chaîne dans laquelle on cherche, re l'expression régulière et m de type smatch (ou cmatch) permet de récupérer les correspondances. Dans l'exemple ci-dessus, s'il y a correspondance, on aura :
m[0]
: la chaîne entière
m[1]
: ftp ou http
m[2]
: vraisemblablement ftp ou www ou un sous domaine
m[3]
: le nom du site
m[4]
: le suffixe com fr net etc.
//compile: !g++ -std=c++11 -Wfatal-errors -o regex_match regex_match.cpp
#include <iostream> // pour cout
#include <string> // pour string
#include <regex> // but de l'article
using namespace std;
//----------------------------------------------------
int main()
{
string s = "ftp://ftp.arad.free.fr";
//string s = "http://www.arad.free.fr";
smatch m; // string matches
cout << "matches : ";
regex re("(http|ftp)://([^.]+)\\.([^.]+)\\.(.+)");
// The entiere string s must match the régular expression
if(regex_match(s, m, re))
for(unsigned c = 0; c < m.size(); c++)
cout << "m[" << c << "] = " << m[c] << endl;
cout << endl;
return 0;
}
Source : regex_match.cpp
Exemple permettant de reconnaitre la notation utilisée par lilypond pour représenter une note :
regex re("([a-g](?:es)?(?:is)?)([',]{0,3})([0-9]{0,3})([.]?)");
Une note est composée d'une lettre [a-g] suivie de 'is' ou 'es' suivie d'une apostrophe ou d'une virgule, suivie d'un nombre, suivi d'un point. Sauf la lettre de départ, tout le reste est facultatif.
La différence avec match, est que search cherche à l'intérieur de la cible. Dans l'exemple ci-dessous on trouve deux occurences du patron recherché.
string = " { ais4 c. }"
smatch m;
regex_search(s, m, ex)
Les méthodes prefix() et suffix() permettent en plus connaître la chaîne qui précède la coïncidence et celle qui la suit.
A la fin du while, on exploite la méthode suffix() pour poursuivre la recherche.
//compile: !g++ -std=c++11 -Wfatal-errors -o regex_match regex_match.cpp
#include <iostream> // pour cout
#include <string> // pour string
#include <regex> // but de l'article
using namespace std;
//----------------------------------------------------
int main()
{
string s = "Le site http://www.arad.free.fr a été rédigé avec Qlam : http://www.arad.free.fr/qlam;";
//string s = "http://www.arad.free.fr";
smatch m; // string matches
regex re("(http|ftp)://([^.]+)\\.([^.]+)\\.([^. ]+)");
// The entiere string s must match the régular expression
while(regex_search(s, m, re)) {
cout << "Prefix: " << m.prefix().str() << endl;
for(unsigned c = 0; c < m.size(); c++)
cout << "m[" << c << "] = " << m[c] << endl;
cout << "Suffix: " << m.suffix().str() << endl;
s = m.suffix().str();
}
return 0;
}
Source : regex_search.cpp.
#include <iostream>
#include <string>
#include <regex>
#include <map>
using namespace std;
/*
map char html_entitie
first : char to replace
second : string replacement
*/
map <string, string> dict = {
{"<", "<"}, {">", ">"},
{"\\[", "["}, {"\\]", "]"},
{"/", ""}, {"#", "#"},
{"\\*", ""}, {"_", "_"}
}; // dict
/*
syntax: s2 = dict_replace(s1);
replace in s1 <, >, [, ], /, * _ by html_dict_replace
*/
string dict_replace(string s)
{
for(auto& x: dict) { // for each x in dict
s = regex_replace(s, regex(x.first), x.second);
}
return s;
}
//----------------------------------------------------
int main()
{
string s;
do {
cin >> s;
if(s == "bye") break;
cout << s << " -> " << dict_replace(s) <<endl;
} while(true);
return 0;
}
Fchier source : regex_replace.cpp
map <string, string> dict
crée un conteneur associatif (comme le dict de python). On peut utiliser .first
et .second
pour la clef et la valeur. for(auto x: dict)
signifie pour tout x dans dict. Avec auto, on est dispensé d'indiquer le type. C'est utile dans certains cas où le type est déterminé par le compilateur. regex_replace
prend trois paratmètres : la cible, la regex et le remplaçant.