234 votes

Les expressions régulières peuvent-elles être utilisées pour faire correspondre des motifs imbriqués ?

Est-il possible d'écrire une expression régulière qui correspond à un motif imbriqué qui apparaît un nombre inconnu de fois ? Par exemple, une expression régulière peut-elle faire correspondre une accolade ouvrante et une accolade fermante lorsqu'il existe un nombre inconnu d'accolades ouvrantes/fermantes imbriquées dans les accolades extérieures ?

Par exemple :

public MyMethod()
{
  if (test)
  {
    // More { }
  }

  // More { }
} // End

Devrait correspondre :

{
  if (test)
  {
    // More { }
  }

  // More { }
}

28 votes

Pour répondre sans ambiguïté à cette question, il faut d'abord définir le terme : "expression régulière".

5 votes

Dans les livres, expressions régulières ne peut pas le faire, mais expressions sans contexte peut. À partir des outils, les analyseurs d'expression modernes appellent regular expression quelque chose qui utilise une pile externe, c'est-à-dire qui est capable de revenir en arrière, qui est capable de faire des recherches : ce sont context-free expressions dans la pratique et vous pouvez donc le faire en une seule ligne avec des simili- PCRE2 (PHP, Java, .NET, Perl, ...) ou UNITÉ DE SOINS INTENSIFS -(Obj-C/Swift), souvent avec l'aide de l'outil d'analyse de l'environnement. (?>...) ou des alternatives telles que la syntaxe (?R) o (?0) syntaxes.

269voto

Torsten Marek Points 27554

Non. C'est aussi simple que ça. Un automate fini (qui est la structure de données sous-jacente à une expression régulière) n'a pas de mémoire en dehors de l'état dans lequel il se trouve, et si vous avez une imbrication arbitrairement profonde, vous avez besoin d'un automate arbitrairement grand, ce qui entre en collision avec la notion de finie automate.

Vous pouvez faire correspondre des éléments imbriqués/appariés jusqu'à une profondeur fixe, la profondeur n'étant limitée que par votre mémoire, car l'automate devient très grand. En pratique, cependant, vous devriez utiliser un automate push-down, c'est-à-dire un analyseur syntaxique pour une grammaire sans contexte, par exemple LL (top-down) ou LR (bottom-up). Vous devez prendre en compte le plus mauvais comportement à l'exécution : O(n^3) vs. O(n), avec n = longueur(entrée).

Il existe de nombreux générateurs d'analyseurs disponibles, par exemple ANTLR pour Java. Il n'est pas non plus difficile de trouver une grammaire existante pour Java (ou C).
Pour plus d'informations : Théorie des automates à Wikipedia

62 votes

Torsten a raison en ce qui concerne la théorie. En pratique, de nombreuses implémentations ont une astuce pour vous permettre d'effectuer des "expressions régulières" récursives. Par exemple, voir le chapitre "Recursive patterns" dans php.net/manuel/fr/regexp.référence.php

2 votes

Je suis gâté par mon éducation en traitement du langage naturel et la théorie des automates qu'elle incluait.

6 votes

Une réponse d'une clarté rafraîchissante. Le meilleur "pourquoi pas" que j'ai jamais vu.

36voto

MichaelRushton Points 7257

L'utilisation d'expressions régulières pour vérifier les motifs imbriqués est très simple.

'/(\((?>[^()]+|(?1))*\))/'

2 votes

Je suis d'accord. Cependant, un problème avec le (?>...) La syntaxe des groupes atomiques (sous PHP 5.2) est que l'élément ?> est interprété comme : "end-of-script" ! Voici comment je l'écrirais : /\((?:[^()]++|(?R))*+\)/ . Cette méthode est un peu plus efficace, tant pour la correspondance que pour la non-coïncidence. Dans sa forme minimale, /\(([^()]|(?R))*\)/ c'est vraiment une belle chose !

1 votes

Double + ? J'ai utilisé (?1) pour permettre aux commentaires de se trouver à l'intérieur d'un autre texte (je l'ai arraché et simplifié à partir de l'expression régulière de mon adresse électronique). Et (?> a été utilisé parce que je pense qu'il permet d'échouer plus rapidement (si nécessaire). N'est-ce pas correct ?

13 votes

Pouvez-vous ajouter une explication pour chaque partie de la regex ?

33voto

Zsolt Botykai Points 20615

Solution Perl probablement fonctionnelle, si la chaîne est sur une seule ligne :

my $NesteD ;
$NesteD = qr/ \{( [^{}] | (??{ $NesteD }) )* \} /x ;

if ( $Stringy =~ m/\b( \w+$NesteD )/x ) {
    print "Found: $1\n" ;
  }

HTH

EDIT : vérifier :

Et encore une chose par Torsten Marek (qui avait fait remarquer, à juste titre, que ce n'est plus une regex) :

9 votes

Yup. Les "expressions régulières" de Perl ne le sont pas (et ne le sont plus depuis très longtemps). Il faut noter que les regex récursifs sont une nouvelle fonctionnalité de Perl 5.10 et que même si vous pouvez le faire, vous ne devriez probablement pas le faire dans la plupart des cas qui se présentent couramment (par exemple, l'analyse du HTML).

1 votes

17voto

Rafał Dowgird Points 16600

Le site Lemme de pompage pour les langages réguliers est la raison pour laquelle vous ne pouvez pas faire ça.

L'automate généré aura un nombre fini d'états, disons k, donc une chaîne de k+1 accolades ouvrantes aura forcément un état répété quelque part (lorsque l'automate traite les caractères). La partie de la chaîne entre le même état peut être dupliquée une infinité de fois et l'automate ne fera pas la différence.

En particulier, s'il accepte k+1 accolades ouvrantes suivies de k+1 accolades fermantes (ce qu'il devrait faire), il acceptera également le nombre pompé d'accolades ouvrantes suivies de k+1 accolades fermantes inchangées (ce qu'il ne devrait pas faire).

13voto

Remo.D Points 9841

Les expressions régulières appropriées ne pourraient pas le faire, car vous quitteriez le domaine des langages réguliers pour atterrir dans les territoires des langages sans contexte.

Néanmoins, les paquets "expression régulière" proposés par de nombreux langages sont nettement plus puissants.

Par exemple, Lua Les expressions régulières ont le caractère " %b() "qui correspondra aux parenthèses équilibrées. Dans votre cas, vous utiliseriez " %b{} "

Un autre outil sophistiqué similaire à sed est gema où il est très facile de faire correspondre des accolades équilibrées avec les éléments suivants {#} .

Ainsi, selon les outils dont vous disposez, votre "expression régulière" (au sens large) peut être capable de correspondre à des parenthèses imbriquées.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X