Generation de l'expression reguliere (regexp) pour manger jusqu'a une chaine

Contenu du snippet

Bon je sais pas vous mais moi ça me gonfle de devoir écrire l'expression reguliere qui signifie : "manger jusqu'a" !

Exemple :
1) Vous souhaitez chercher une balise html
<truc ... >
Facile ! C'est "<[^>]*>"

2) Vous souhaitez chercher un commentaire en c++, php, ...
/* commentaire [sur plusieurs lignes] */
Heu attend je réfléchi ... arf galère

3) Vous souhaitez chercher des commentaires html
<!-- ... -->
Heu attend .. AU SECOUR !!!

Bon clairement ca devient vite infernal !
Donc le but de cette source est de générer la regexp qui va manger tout jusqu'à une chaine précise (la chaine est inclue dans le repas de la regexp :p)
Les expressions régulières requise pour les exemples ci-dessus s'écriront donc :
1) "<" + UNTIL_WITH(">");
2) "/\*" + UNTIL_WITH("*/");
2) "<!--" + UNTIL_WITH("-->");

Carrément plus simple non ? :)
Le code est un peu astucieux et faut réfléchir pour le comprendre ...
Bon je sais ... vous allez tous pomper le code et basta X-D !

La fonction UNTIL_WITH prend en argument une chaine normale,
les caractères spéciaux n'ont pas besoin d'être échappé.
Une variable static recense les caracteres qui seront echappé automatiquement.

Les autres variables static sont à modifier par vos soins !
(super rapide normalement si vous connaissez les regexp)

J'ai fait une autre fonction UNTILS_WITH (notez le 'S' à UNTIL) qui génère une regexp qui mange tout jusqu'à tomber sur une des deux chaines.
Exemple : UNTILS_WITH("fin", "end");
La fonction est EXTREMEMENT simple car elle utilise juste UNTIL_WITH !
En regardant la source vous pourrez faire une liste aussi longue que vous le souhaitez.

A noter enfin qu'au cas ou une chaine est prefixe d'une autre cela ne pose pas de probleme,
c'est la chaine la plus longue qui sera manger à la fin.

Source / Exemple :


#include <string>
#include <iostream>

using namespace std;

static const string to_escape   = "^\\*+()[]{}$"; // Tous les caracteres qui ont besoin d'etre echapp?
static const char   escape_char = '\\';
static const string parent_open   = "(?:"; // Parenthese ouvrante non capturante pour ne pas gener les matchs, (en general "(?:")
static const char   parent_close  = ')';
static const char   bracket_open  = '[';
static const char   bracket_close = ']'; // A placer au debut de classe d'intervalle
static const char   bracket_not   = '^'; // Parfois egal a '!'

typedef string (*func)(const string& word, int length);

// Echappe simplement les caracteres
// Contrainte : length >= 1 && word.length >= length

static string ESCAPE(const string& word, int length)
{
  string res;

  for (int i = 0; i < length; ++i)
  {
    if (string::npos != to_escape.find(word[i]))
      res += escape_char;
    res += word[i];
  }
  return res;
}

// Appelle ESCAPE
// ET ajoute une subtilit?
// Contrainte : <les memes que ESCAPE>
static string WITH(const string& word, int length)
{
  string res = ESCAPE(word, length);
  return res.insert(1 + (res[0] == escape_char), 1, '+'); // LA petite subtilit? :)
}

// Permet de construire une regexp qui mange tout et s'arrete apres une chaine donn?e
// Exemple pour matcher un commentaire en c++ : "/*" + UNTIL("*/") + "*/"
// Facile non ?
static string UNTIL_WITH(const string& word)
{
  unsigned int n = word.length();
  string res = n > 1 ? parent_open : "";
  unsigned int i = 1;

  while (i < word.length() && word[0] == word[i])
      ++i;
  func Subtility = i < word.length() ? WITH : ESCAPE;

  // Optionnel, il s'agit d'un pronostic sur la taille probable
  // Ce pronostic optimise un peu ...
  // Le pronostic est inferieur ? la taille r?elle si des caract?res doivent etre echapp? !
  // Comptons les caracteres ( n == word.length() )
  // 1 : parenthese ouvrante
  // n - 1 : Nombre de '|'
  // n * 3 : Nombre de '[^]'
  // n * 2 - 1 : Nombre de caracteres dans les crochets
  // (n-1) * n / 2 + n -1 : Nombre de caracteres en dehors des crochets (somme de 0 a n - 1) et n-1 fois le '+'
  // 1 + 1 : parenthese fermante et etoile
  // n + 1 : Mot a manger a la fin avec le '+'
  // Soit au final :
  // {
   res.reserve((15 + n) * n / 2 + 1);
  // Version ou le pronostic >= taille reelle (considere que tous les caracteres ont besoin d'etre echapp?s) !
  // res.reserve((n + 9) * n);
  // }

  for (i = 0; i < n; ++i)
  {
    if (i)
    {
      res += '|';
      res += Subtility(word, i);
    }
    res += bracket_open;
    res += bracket_not;
    if (word[0] == word[i])
      res += word[i];
    else if (word[i] == bracket_close)
    {
      res += word[i];
      res += word[0];
    }
    else
    {
      res += word[0];
      res += word[i];
    }
    res += bracket_close;
  }
  if (n > 1)
    res += parent_close;
  res += '*' + Subtility(word, i);
  return res;
}

// M?me chose que UNTIL mais pour s'arreter apres la premiere des deux chaines trouv?e
// Cette fonction est la fonction de base permetant de construire des listes de chaines devant lesquelles s'arreter.
// ( Certes UNTIL avec S n'existe pas mais tant pis :p )
static string UNTILS_WITH(const string& word_1, const string& word_2)
{
  return parent_open + UNTIL_WITH(word_1) + "|" + UNTIL_WITH(word_2) + parent_close;
}

int main()
{
  cout << "Commentaire C++, php ... :" << endl;
=>cout << "/\\*" + UNTIL_WITH("*/") << endl;
  cout << endl;
  cout << endl;

  // Matcher un commentaire HTML selon la norme W3C
  // format : <!-- blablabla -- blablabla >
  // Et oui ! normalement -- et > ne sont pas forcement coll? :
  cout << "Commentaire HTML (selon la norme W3C) :" << endl;
  cout << "<!--" + UNTIL_WITH("--") + UNTIL_WITH(">") << endl;
  cout << endl;
  cout << endl;

  cout << "jusqu'a \"abc\" :" << endl;
  cout << UNTIL_WITH("abc") << endl;
  cout << endl;
  cout << endl;

  return EXIT_SUCCESS;
}

Conclusion :


Si y'a des bug hésitez pas !
J'ai pas super testé la fonction UNTIL_WITH.
Juste dans ma tête et dans le main ...

La source est dans la catégorie "Astuce" et non pas "Chaine de caractère" ...
Ai-je bien fait ?

A voir également

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.