02/09/2015

Regex en C++

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

Sources

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:

Exemples :

"^# (.*)$"
le caractère dièse au début, suivi d'un espce, suivi de n'importe quoi. Ce n'importe quoi forme un groupe référencé par $1.
Exemple # commentaire
"(\\+|\\-)? (.*)$"
Plus ou moins ou rien suivi d'espace puis n'importe quoi.
Exemple "+ Ligne n° 1".
Remarque le double anti slash n'en produit qu'un seul.
"\\[(.+?)->(.+?)\\]"
On voudrait bien quelque chose comme 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

Autre exemple

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 = {
	{"<", "&lt;"}, {">", "&gt;"},
	{"\\[", "&#91;"}, {"\\]", "&#93;"},
	{"/", "&#17;"}, {"#", "&num;"},
	{"\\*", "&#12;"}, {"_", "&#95;"}
}; // 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

Commentaires



Réalisé avec Qlam - LGPL