2010-08-29 7 views
4

L'entrée est une chaîne représentant une liste d'éléments.Trouver la parenthèse correspondante avec Regex

Une liste est définie comme une boucle ouverte { suivie par 0 ou plusieurs éléments séparés par des espaces suivis d'un bouclé fermé }.

Un élément est soit un littéral, soit une liste d'éléments.

Un littéral est une succession de caractères qui ne sont pas des espaces. Si un élément contient une parenthèse bouclée, il doit être échappé avec un antislash: \{ et \}. (Ou vous pourriez supposer Curlies ne sont pas autorisés à l'intérieur littéraux, pour simplifier) ​​

Exemple:

"{abc { def ghi } 7 { 1 {2} {3 4} } {5 6} x\{yz \}foo }" 

Pas Curly à l'intérieur littéraux:

"{abc { def ghi } 7 { 1 {2} {3 4} } {5 6} xyz foo }" 

(Ceci est une définition simplifiée d'une liste Tcl Ce que je veux savoir est: l'entrée peut-elle être divisée en éléments de la boucle la plus externe en utilisant regex?

Résultats attendus:

abc 
{ def ghi } 
7 
{ 1 {2} {3 4} } 
{5 6} 
x{yz 
}foo 

La vraie question est: peut-il être fait avec un Regex?

Je suis plus intéressé par la saveur .NET, mais acceptera toutes les réponses.

Je vais poster ma propre hypothèse dans une réponse, et voir si elle est validée ou détruite.

+1

Pourquoi '} foo' un littéral, mais' 4} 'est pas? En fait, '}' est un littéral valide selon votre définition. – Kobi

+0

@Kobi vous avez raison. J'essayais d'obtenir une définition similaire à celle de l'interpréteur Tcl, mais ça fait des choses étranges. Par exemple, cela permettra 'set a 3 {4' mais pas' set a {1 2 3 {4} '. Comportement similaire pour les curlies fermés. Je vais mettre à jour la question. –

Répondre

3

Eh bien, l'édition supprime les accolades des jetons et prend l'aiguillon de la question, et maintenant il est facilement réalisable avec .Net Regex, en utilisant des groupes d'équilibrage. Il s'agit simplement d'accolades correspondantes, ce qui est un exemple fondamental.
Tout comme la réponse de KennyTM, cela ne fonctionnera que si vous supprimez les accolades de niveau supérieur, ou il correspondra à l'ensemble de l'entrée.
Encore une fois, cela est mieux utilisé à des fins récréatives:

(?:     # try matching... 
    (?:\\[{}]|[^\s{}])+\s*? # a literal (allow escaped curly braces) 
    |      # OR 
    (?<Curly>{)\s*   # "{" and push to stack 
    |      # OR 
    (?<-Curly>})\s*?  # "}", pop from stack and fail if the stack is empty 
)+?     # ...a few times, and stop whenever you can. 
(?(Curly)(?!))   # Make sure there aren't any extra open curly braces 

Pour plus de détails beaucoup plus voir cet article: Regex Balancing Group in Depth

+0

Avant la mise à jour de la question, je n'arrivais pas à la faire fonctionner. Sur l'autre question, nous * validons * du début à la fin ('^ (?: ...) + $'), donc le moteur doit essayer chaque combinaison. Cependant, lorsque vous * correspondez * pour des jetons, le moteur peut satisfaire avec moins, et il est difficile de définir des priorités. – Kobi

2

La réponse traditionnelle à ceci est un "NO" retentissant. Comme nous l'avons appris dans la classe des compilateurs, une grammaire régulière ne peut pas être utilisée pour décrire un langage avec une définition récursive (c'est-à-dire que vous ne pouvez pas utiliser une machine d'états finis)

dont la mise en œuvre se résume à une machine à états finis + un STACK.
Voir ANTLR, bison etc.

+2

La prochaine fois que vous pourriez vouloir laisser quelques minutes avant de poster votre réponse car il est difficile pour d'autres personnes d'obtenir des votes s'il y a déjà une publication mise à jour, ceci peut décourager beaucoup d'autres personnes de poster ... ou même de en regardant la question (si la question est déjà posée, beaucoup de gens ne la verront même pas). Je suppose que vous êtes intéressé à recevoir d'autres opinions, sinon vous n'auriez pas posté ... non? PS: dans .NET Je crois qu'il est possible de le faire en utilisant des expressions "régulières" mais vous avez raison de dire qu'il n'est pas conseillé d'utiliser regex à cette fin. –

+0

@mark Remarque prise. Et oui, je suis très intéressé par les réponses. Je me souviens d'avoir lu quelque part des extensions moins orthodoxes à une bibliothèque regex qui permet de faire correspondre des parens dans certaines circonstances, mais je ne me souviens pas de quelle bibliothèque ou de quelles circonstances ... –

+0

Je ne peux pas suivre ça. Les gens devraient attendre avant d'afficher les bonnes réponses? Les expressions régulières peuvent-elles être utilisées pour les langues nécessitant un DPDA? – EJP

1

@Cristi est juste au sujet de la regex: Il est théoriquement impossible de résoudre les expressions récursives utilisant un stackless, automate à états finis. La solution, cependant, est plus simple: il suffit de garder un compteur du nombre de parenthèses ouvertes, et assurez-vous qu'il ne descend pas au-dessous de 0. Il est plus économique de conserver la pile et vous n'avez besoin que de compte - pas le contenu - des parenthèses.

algorithme:

counter = 0      // Number of open parens 
For char c in string:    
    print c       
    if c=='{':      // Keep track on number of open parens 
     counter++ 
    if c=='}': 
     counter-- 
    if counter==1:     // New line if we're back to the top level 
     print "\n" 
    elif counter<1:    // Error if the nesting is malformed 
     print "ERROR: parentheses mismatch" 
     break 
+0

Ceci ne tient pas compte des curlies échappées ... –

+0

C'est vrai, mais la correction est assez simple. –

4

Malheureusement, la réponse est OUI pour une saveur de Regex, par exemple PCRE et .NET, car ils prennent en charge respectivement des opérations récursives et des opérations de type pile.

La grammaire peut être écrite comme

ELEMENT -> (?!\{)\S+ | LIST 
LIST  -> '\{\s*' ELEMENT? ('\s+' ELEMENT)* '\s*\}' 

ainsi dans PCRE, cela peut être transformé en modèle:

\{\s*(?0)?(?:\s+(?0))*\s*\}|(?!\{)(?:[^\s}]|\}(?![\s}]))+ 

# ---------------------------     ^^^^^^^^^ 
#   LIST     Make sure the } is not closing the group 

Voir http://www.ideone.com/SnGsU par exemple (je privais de haut niveau { et } pour plus de simplicité).

(Bien sûr, ne pas essayer au travail :))

(BTW, je ne sais pas comment transformer ce PCRE en saveur .NET. Si quelqu'un sait, s'il vous plaît essayer Converting PCRE recursive regex pattern to .NET balancing groups definition)

+0

Wow! Juste une question: dans votre définition grammaticale, que signifie (?! \ {) Au début de ELEMENT? –

+0

@Cristi: C'est un [lookahead négatif] (http://www.regular-expressions.info/lookaround.html). – kennytm

+0

J'aimerais pouvoir choisir deux réponses comme «réponse acceptée», car celle-ci est plutôt complète. Cependant, la réponse de Kobi correspond mieux à ce que je cherchais, et la regex est plus lisible. –

Questions connexes