2010-07-12 6 views
1

Supposons que vous voulez faire correspondre le texte qui est délimité par des caractères doubles comme ceci: (. *)assorti doubles ombles chaînes délimitées avec des expressions régulières

a = << 
Hello 
World! 
>> 

L'expression régulière/< < >>/sembleraient faire , mais malheureusement, quand ceux-ci peuvent être répétées correspondant avide devient trop:

a = << 
Hello 
World! 
>> 

b = << 
Goodbye 
World! 
>> 

précédent regexp capture

Hello 
World! 
>> 

b = << 
Goodbye 
World! 

La réponse évidente est de faire le regexp non gourmand: (. *?)/< < >>/

Malheureusement, cela a des problèmes de performances extrêmes pour les longues chaînes (en Perl au moins). Si les délimiteurs étaient des caractères uniques, nous pourrions utiliser une classe de caractères (tout sauf le caractère) pour résoudre le problème glouton.

Des idées sur une expression régulière pour faire correspondre ce match sans la pénalité de performance?

NB: je dois utiliser Perl et cela doit être une expression régulière en raison du système plus grand, il est intégré dans

Merci..

+4

Phil - pourriez-vous élaborer sur «doit être une expression régulière en raison du plus grand système dans lequel il est intégré»? Cela semble être un travail mieux adapté à un analyseur (tel que Text :: Balanced) qu'un RegEx. – DVK

+0

Essayez de regarder la deuxième réponse à cette question: http://stackoverflow.com/questions/2975950/is-it-better-to-use-a-non-greedy-qualifier-or-a-lookahead – Josiah

+0

Votre affirmation que Faire une regex non gourmande "a des problèmes de performances extrêmes" me semblait contre-intuitif. J'aurais pensé qu'un non-gourmand devrait être plus rapide puisqu'il peut s'arrêter au premier match alors que la version gourmande doit continuer et éventuellement revenir en arrière. J'ai donc fait quelques tests avec différentes longueurs de cordes et expressions régulières et je n'ai jamais vu le problème de performance auquel vous vous référez. Utilisez-vous un match ancré dans votre regex? –

Répondre

1

Veuillez voir si les performances d'un analyseur dédié (tel que Text::Balanced) seraient acceptables dans ce cas. Ce n'est pas regex, mais sans plus de détails sur votre post-scriptum "NB" il semble que vous pourriez avoir un XY problem lorsque vous cherchez une solution regex seulement.

Si vous devez absolument utiliser une regex, veuillez utiliser une fonctionnalité de prévisualisation - cela peut améliorer la vitesse.

+0

Quand vous dites "fonctionnalité de prévisualisation" pouvez-vous élaborer? Je pense que le groupe de capture aurait le quantificateur de gourmandise et le lookahead ne résout pas cela, non? c'est-à-dire que toute variante de lookahead positive, regarder derrière, devra encore limiter la portée de la correspondance. '/ <<(.*)(?=>>) /' aura toujours le même problème de correspondance avec le délimiteur final. La seule solution facile que je vois est une classe de caractères négative sur le premier caractère du délimiteur de fermeture. '<<([^>] *) >>' est aussi efficace que '<<(.*)>>' en termes de nombre total de sondes et donne la bonne réponse. – dawg

+0

Text :: Balanced est cool mais je ne pense pas que cela corresponde vraiment à ce problème, parce que OP n'a pas demandé d'imbrication et parce que Text :: Balanced est vraiment destiné à traiter des délimiteurs à un seul caractère et à construire comme backslashing. – hobbs

+0

BTW, j'ai essayé lookahead et cela fonctionne, mais avec des performances identiques à la spécification regexp non gourmande. –

2

Utilisation d'une classe de caractères niée dans ce cas ne fonctionnera: cependant

/<<([^>]*)>>/ est la même sonde nombre que /<<(.*)>>/ devrait donc être tout aussi rapide avec moins de retours en arrière comme /<<(.*?)>>/

Je suis d'accord avec DVK; est une regex le seul moyen?

+0

Sauf que> peut se produire à l'intérieur des <<...>> délimiteurs: a = << hey >> –

+0

ahmm, qui n'a pas été spécifié dans votre poste. Est-ce que votre analyse HTML avec une regex? S'il vous plaît regardez http://stackoverflow.com/questions/1732348/regex-match-open-tags-except-xhtml-self-contained-tags/1732454#1732454 pour une discussion sur pourquoi ce n'est pas une bonne idée. – dawg

+0

Non, n'essayant pas d'analyser HTML. Je comprends les dangers là-bas. Juste essayer de tout consommer entre les délimiteurs (y compris les espaces blancs). Désolé de ne pas avoir spécifié le besoin de> à l'intérieur des délimiteurs. –

4

L'expansion de la réponse de drewk il fonctionne réellement:

/<<((?:(?>[^>]+)|>(?!>))*)>>/ 

Match « < < », puis une séquence de 0 ou plusieurs morceaux qui sont soit un nombre quelconque de non - caractères « > », ou un " > "pas suivi d'un autre"> ", puis finalement" >> ".

+0

+1: Oui cela fonctionne, mais presque le même nombre de sondes que '<<(.*?)>>', non? Au moins sur ma regex sim ... – dawg

+0

Si c'est le cas, alors je suis sûr qu'ils sont tous deux aussi efficaces que possible, parce que c'est vraiment une représentation minimale de la machine d'état pour correspondre '<<'..'>>' -delimited des trucs. – hobbs

+0

@drewk J'ai fait un changement qui devrait réduire la quantité de retour arrière que le modèle est autorisé à faire - cela aide-t-il? – hobbs

1

Supposons que vous avez une grammaire simple

my $p = Parse::RecDescent->new(<<'EOGrammar'); 
    program: assignment(s) 

    assignment: id '=' '<<' angle_text '>>' 
       { $return = [ $item{id}, $item{angle_text} ] } 

    angle_text: <skip:undef>/([^>] | >(?!>))* /x 

    id: /\w+/ 
EOGrammar 

et un texte source

a = << 
Hello 

World! 

>> 

b = << 


Goodbye 
World! 
>> 

Lorsque vous traitez le résultat avec

for (@{ $p->program($text) }) { 
    my($name,$what) = @$_; 
    print "$name: [[[$what]]]\n"; 
} 

vous verrez la sortie de

a: [[[ 
Hello 

World! 

]]] 
b: [[[ 


Goodbye 
World! 
]]]
3

Utilisez-vous Perl 5.10? Essayez ceci:

/<<([^>]*+(?:>(?!>)[^>]*+)*+)>>/ 

Comme les @hobbs regex affichés, celui-ci effectue seulement après qu'il trouve préanalyse une > (par opposition à la quantificateur non gourmand, qui réalise effectivement une préanalyse à chaque position). Mais celui-ci utilise la technique de «boucle déroulée» de Friedl, qui devrait être légèrement plus rapide que l'approche de l'alternance. De plus, tous les quantificateurs sont possessifs, donc cela ne dérange pas de sauvegarder les informations d'état qui rendraient le retour en arrière possible.

+0

+1: Sweet regex! Il gère toujours le cas que je peux penser et il aussi vite que '/ <<(.*)>> /' Devrait être la réponse à mon humble avis. – dawg

Questions connexes