1 votes

Une expression rationnelle avec une partie optionnelle ne crée pas de référence arrière.

Je veux faire correspondre une balise optionnelle à la fin d'une ligne de texte.

Exemple de texte d'entrée :

The quick brown fox jumps over the lazy dog {tag}

Je veux faire correspondre la partie entre accolades et créer une référence arrière.

Ma requête ressemble à ceci :

^.*(\{\w+\})?

(quelque peu simplifié, je fais également correspondre les parties avant l'étiquette) :

Il fait correspondre les lignes correctement (avec et sans la balise) mais ne crée pas de référence arrière à la balise.

Si je supprime le caractère '?', la regex est donc :

^.*(\{\w+\})

Il crée une référence arrière à la balise mais ne fait pas correspondre les lignes sans la balise.

J'ai compris de http://www.regular-expressions.info/refadv.html que l'opérateur optionnel n'affecterait pas la référence arrière :

Les crochets ronds regroupent les expressions rationnelles entre eux. Elles capturent le texte correspondant à la regex qu'elles contiennent et qui peut être réutilisé dans une référence arrière, et elles vous permettent d'appliquer des opérateurs de regex à l'ensemble de la regex groupée.

mais j'ai dû mal comprendre quelque chose.

Comment rendre la partie "tag" facultative et créer une référence arrière lorsqu'elle existe ?

3voto

David Gladfelter Points 2756

Il ne s'agit pas d'un problème de référence arrière, le problème est que l'expression régulière a été satisfaite en lisant simplement le texte qui correspondait à .* . Il ne s'est pas senti obligé de continuer à lire pour lire la balise de fin optionnelle. La solution la plus simple si vous lisez vraiment jusqu'à la fin de la ligne est d'ajouter une balise $ (signe du dollar) pour forcer l'expression régulière à correspondre à la ligne entière.

éditer

BTW, je n'ai pas pris votre reg-ex au pied de la lettre puisque vous avez dit qu'il correspondait à d'autres choses, mais juste pour être clair .* consommera toute la ligne. Vous auriez besoin de quelque chose comme [^{]* pour éviter que l'étiquette ne soit avalée. Je suppose que ce n'est pas un problème pour vous.

2voto

Toby Points 4112

En plus de ce qui a été expliqué par d'autres, vous voudrez peut-être faire de la .* "paresseux" :

^.*?(\{\w+\})?

1voto

Antal S-Z Points 17977

Comme l'a dit David Gladfelter, le problème réel est que lorsque vous les rendez facultatifs, ils ne correspondent pas à la réalité. Cependant, la solution qu'il propose ne fonctionnera pas. . Editer 1 : Vous devrez utiliser ce qu'il a mis dans son édition (qui a été rédigée pendant que j'écrivais ceci). Le problème est que les quantificateurs ( * , + , ? , {n,m} ) sont avide Ils s'accordent toujours, dans la mesure du possible, avec les autres. Ainsi, lorsque vous écrivez ^.*(\{\w+\})? , le .* correspondra toujours à la ligne entière, car une correspondance vide satisfait le groupe optionnel. Notez également que même si ? est gourmande, la première gourmandise (de .* ) est prioritaire. Si vous n'avez le droit d'utiliser que des parenthèses courbes autour de ce groupe optionnel, vous pouvez résoudre votre problème en le disant explicitement : ^[^\{]*(\{\w+\})? . De cette façon, le premier bloc correspondra à tout ce qui se trouve dans le premier crochet, et ensuite (puisque ? est gourmand) correspond au mot entre crochets s'il le peut.

Souvent, une autre façon de résoudre ce problème est de rendre les quantificateurs paresseux (ou non gourmande, minimale, etc.) en ajoutant un élément ? : *? , +? , ?? y {n,m}? . Toutefois, cela ne vous sera d'aucune utilité : si vous faites ^.*?(\{\w+\})? , les paresseux .*? essaiera de faire correspondre zéro caractère, réussira, puis le groupe optionnel ne correspondra pas. Bien qu'il ne fonctionne pas ici, c'est un outil utile dans votre boîte à outils. Editer 1 : Notez également que ces fonctions ne sont pas disponibles dans tous les moteurs d'expressions rationnelles, bien qu'elles le soient en C#.

1voto

user3891 Points 2641

Merci les gars. J'ai utilisé une combinaison de réponses, le modificateur "not-greedy" et la correspondance en fin de ligne, ce qui semble faire l'affaire :

^.*?(\{\w+\})?$ 

Je n'ai pas voulu utiliser [^{]* pour la première partie de la correspondance, car des parenthèses non-tag peuvent apparaître ici, mais les tags seront toujours à la fin de la ligne.

Merci pour les réponses, elles ont toutes été utiles.

0voto

polygenelubricants Points 136838

Si vous n'êtes intéressé que par la balise, et que vous ne vous souciez pas du reste de la chaîne, alors vous vous faciliterez la vie en faisant correspondre la balise avec cette regex ( voir sur rubular.com ) :

\{(\w+)\}$

En d'autres termes, vous essayez de faire correspondre des {word} à la fin de la chaîne. Si ce n'est pas le cas, tant pis, il n'y a pas de correspondance. Il n'y a pas besoin d'un ? ou une personne réticente .* et toutes ces choses.

En C#, vous pouvez même utiliser RegexOptions.RightToLeft puisque vous essayez de faire correspondre un suffixe de toute façon, alors peut-être quelque chose comme ceci :

string[] lines = {
  "The quick brown fox jumps over the lazy dog",
  "The quick brown fox jumps over the lazy dog {tag}",
  "The quick brown fox jumps over the {lazy} dog",
  "The quick brown fox jumps over the {lazy} {dog}",
};

Regex r = new Regex(@"\{(\w+)\}$", RegexOptions.RightToLeft);

foreach (string line in lines) {
  Console.WriteLine("[" + r.Match(line).Groups[1] + "]");
}

Ceci imprime ( tel que vu sur ideone.com ) :

[]
[tag]
[]
[dog]

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