L'effondrement et de la Capture d'un Motif répété dans une Seule Regex Expression
Je continue cogner dans des situations où j'ai besoin d'obtenir un certain nombre de jetons à partir d'une chaîne et après d'innombrables essais, je ne pouvais pas trouver un moyen de simplifier le processus.
Disons donc que le texte est:
début:test-test-lorem ipsum-sir-doloret-etc-etc-chose:la fin
Cet exemple a 8 éléments à l'intérieur, mais dire qu'il pourrait avoir entre 3 et 10 points.
J'avais idéalement comme quelque chose comme ceci:
start:(?:(\w+)-?){3,10}:end
agréable et propre, MAIS il ne saisit du dernier match. voir ici
J'ai l'habitude d'utiliser quelque chose comme cela dans des situations simples:
start:(\w+)-(\w+)-(\w+)-?(\w+)?-?(\w+)?-?(\w+)?-?(\w+)?-?(\w+)?-?(\w+)?-?(\w+)?:end
3 groupes obligatoires et un autre de 7 en option en raison de la max 10 limite, mais cela n'a pas l'air "sympa" et ce serait une douleur à écrire et à suivre si la limite est de 100, et les matchs ont été plus complexe. démo
Et le meilleur que j'ai pu faire jusqu'à présent:
start:(\w+)-((?1))-((?1))-?((?1))?-?((?1))?-?((?1))?-?((?1))?-?((?1))?:end
plus courte, surtout si les matches sont complexes, mais encore long. démo
Quelqu'un a réussi à le faire fonctionner comme un 1 regex seule solution sans programmation?
Je suis surtout intéressé sur la façon dont cela peut être fait en PCRE mais d'autres saveurs serait ok aussi.
Mise à jour:
Le but est de valider un match et de capturer des jetons à l'intérieur de match 0
par RegEx seul, sans OS/logiciel/Logiciel de Programmation de Langue limitation
Mise à jour 2 (bounty):
Avec @nhahtdh de l'aide, je suis arrivé à l'expression régulière ci-dessous en utilisant \G
:
(?:start:(?=(?:[\w]+(?:-|(?=:end))){3,10}:end)|(?!^)\G-)([\w]+)
démo encore plus court, mais peut être décrit sans la répétition du code
Je suis aussi intéressé par l'ECMA saveur et comme il ne supporte pas \G
demandais si il y a une autre façon, surtout sans l'aide de /g
modificateur.
- Les expressions régulières sont vraiment conçus pour la reconnaissance de motifs, mais vous essayez de l'utiliser pour un changement de tendance. Vous ne dites pas quel système d'exploitation vous êtes sur mais un Awk (Unix/Linux) ou Powershell (Windows) serait probablement faire ce que vous devez faire...
- mis à jour le post pour clarifier, la recherche d'une façon intelligente d'utiliser les RegEx dans des situations complexes, sans l'utilisation d'un logiciel d'assistance
- non, vous ne pouvez pas faire général des choses comme ça en JS dans un seul match/étape. Les seules façons de faire qui sont dans:
.NET
(capture de répéter le contenu de groupe), ou avec la regex saveurs qui prennent en charge\G
(ou similaire fonctionnalités de l'API).
Vous devez vous connecter pour publier un commentaire.
De lire ceci en premier!
Ce post est de montrer la possibilité, plutôt que de sanctionner le "tout regex" approche du problème. L'auteur a écrit 3-4 variantes, chacun a bug subtil qui sont difficiles à détecter, avant d'arriver à la solution actuelle.
Pour votre exemple, il y a d'autres meilleure solution est plus facile à gérer, comme l'appariement et le fractionnement du match, le long de la délimiteurs.
Ce post traite avec votre exemple. Je doute vraiment plein de généralisation est possible, mais l'idée derrière est réutilisable pour des cas similaires.
Résumé
CaptureCollection
classe.\G
et de regarder derrière, nous pouvons être en mesure de construire une regex qui fonctionne avec appariement globale de la fonction. Il n'est pas facile de l'écrire tout à fait correcte et facile à écrire un subtil buggy regex.\G
et regardez-derrière: il est possible d'émuler\G
avec^
, en partant de la chaîne d'entrée après un seul match. (Ne sont pas couverts dans cette réponse).Solution
Cette solution suppose que le moteur d'expressions régulières prend en charge
\G
match frontière, look-ahead(?=pattern)
, et regardez-derrière(?<=pattern)
. Java, Perl, PCRE, .NET, Ruby regex saveurs en faveur de ces fonctionnalités avancées ci-dessus.Cependant, vous pouvez aller avec votre regex dans .NET. Depuis .NET prend en charge la capture de toutes les instances de qui est compensée par une capture d'un groupe qui est répété par
CaptureCollection
classe.Pour votre cas, il peut être fait dans une regex, avec l'utilisation de
\G
correspondre à des limites, et regarde en avant pour limiter le nombre de répétitions:DÉMO. La construction est
\w+-
répète, puis\w+:end
.DÉMO. La construction est
\w+
pour le premier élément, puis-\w+
répété. (Merci à ka ᵠ pour la suggestion). Cette construction est plus simple de raisonner sur son exactitude, car il y a moins de postes.\G
match frontière est particulièrement utile lorsque vous devez effectuer la segmentation, où vous devez assurez-vous que le moteur n'est pas d'avancer et de correspondance des étoffes qui doivent avoir été invalide.Explication
Laissez-nous briser les regex:
La partie la plus facile à reconnaître, c'est
(\w+)
dans la ligne de l'avant-dernier, qui est le mot que vous voulez capturer.La dernière ligne est également très facile à reconnaître: le mot à rechercher peut être suivie par
-
ou:end
.J'autorise l'expression régulière regex pour librement début correspondant n'importe où dans la chaîne. En d'autres termes,
start:...:end
peuvent apparaître n'importe où dans la chaîne, et un certain nombre de fois; les regex simplement correspondre à tous les mots. Vous avez seulement besoin de traiter le tableau retourné à séparer le cas de la correspondance des jetons viennent en fait de.Comme pour l'explication, le début de la regex vérifie la présence de la chaîne
start:
, et la suite de look-ahead vérifie que le nombre de mots est à l'intérieur de la limite spécifiée, et il se termine avec:end
. Soit ça, ou nous vérifions que le caractère avant le match précédent est un-
, et continuer de match précédent.Pour les autres constructions:
Tout est presque la même, sauf que nous match
start:\w+
d'abord avant d'égaler la répétition de la forme-\w+
. Contrairement à la première de la construction, où nous matchstart:\w+-
en premier, et les instances répétées de\w+-
(ou\w+:end
pour la dernière répétition).Il est assez difficile à faire cette regex fonctionne pour la mise en correspondance dans le milieu de la chaîne:
Nous avons besoin de vérifier le nombre de mots entre
start:
et:end
(dans le cadre de l'exigence de l'original regex).\G
correspond au début de la chaîne aussi!(?!^)
est nécessaire pour éviter ce problème. Sans prendre soin de cela, les regex peuvent produire un match quand il n'est pas toutstart:
.Pour la première construction, à l'affût derrière
(?<=-)
déjà éviter ce cas ((?!^)
est implicite par(?<=-)
).Pour la première construction
(?:start:(?=\w+(?:-\w+){2,9}:end)|(?<=-)\G)(\w+)(?:-|:end)
, nous devons nous assurer que nous n'avons pas correspondre à quelque chose de drôle après:end
. Le look-derrière est à cette fin: elle empêche les poubelles après:end
à partir de la correspondance.La deuxième construction n'a pas ce problème, puisque nous sommes coincés à
:
(de:end
) après nous avons comparé l'ensemble des jetons entre les deux.De Validation De La Version
Si vous voulez faire que la validation de la chaîne d'entrée suit le format (pas de trucs supplémentaires à l'avant et derrière), et extraire les données, vous pouvez ajouter des ancres en tant que tel:
(Look-derrière n'est pas nécessaire, mais nous avons encore besoin
(?!^)
pour éviter\G
à partir de la correspondance au début de la chaîne).Construction
Pour tous les problèmes de l'endroit où vous souhaitez capturer toutes les instances d'une répétition, je ne pense pas qu'il existe une manière générale, pour modifier la regex. Un exemple d'un "dur" (voire impossible?) cas de conversion est lors d'une répétition a pour revenir en arrière d'une ou de plus de boucle de remplir certaines conditions de match.
Lorsque la première regex décrit l'ensemble de la chaîne d'entrée (type de validation), il est généralement plus facile de convertir par rapport à une regex qui essaie de faire correspondre depuis le milieu de la corde (même type). Cependant, vous pouvez toujours faire un match avec l'original de la regex, et nous convertir correspondance type de problème de dos à la validation type de problème.
Nous construire de tels regex en passant par ces étapes:
start:
). Laissez-nous appeler cette préfixe regex.(\w+)
)(À ce stade, la première instance et délimiteur doit avoir été appariés)
\G
comme une alternance. Besoin d'être pour l'empêcher de correspondre au début de la chaîne.-
)(Après cette étape, le reste des jetons ont également été mis en correspondance, à l'exception de la dernière, peut-être)
:end
). Nous allons l'appeler la partie après la répétition suffixe regex (si nous l'ajoutons à la construction n'a pas d'importance).\G
branche.\G
branche commence un match.:end
) avec séparateur (par exemple-
) dans une alternance, assurez-vous de ne pas permettre le suffixe regex comme délimiteur.start:...:end
d'abord, puis diviser chaque match, le long de la délimiteurs. Dans le cas de votre exemple, le délimiteur est assez clair que vous n'avez même pas besoin de mon laid regex solution.(?!^)\G
est important démo:
est autorisé dans la répétition de jeton, il n'est pas clair si pour correspondre à tout le chemin à la 2ème:end
ou s'arrêter à la première:end
dansstart:some-thing-here:end:end unrelated text
(?=...)
direction générale, vous obtenez toujours les résultats de la droite... quel est Donc le point de l'avoir ? Je ne le vois pas xDBien qu'il pourrait théoriquement être possible d'écrire une expression unique, c'est beaucoup plus pratique pour correspondre à l'extérieur des limites d'abord et ensuite effectuer un split sur la partie intérieure.
Dans ECMAScript je voudrais écrire comme ceci:
En PHP:
Bien sûr, vous pouvez utiliser les regex dans cette chaîne de caractères entre guillemets.
Est-ce une bonne idée? Non, je ne le pense pas.
Pas sûr que vous pouvez le faire de cette façon, mais vous pouvez utiliser l'indicateur global de trouver tous les mots, entre les deux points, voir:
http://regex101.com/r/gK0lX1
Vous auriez à valider le nombre de groupes de même à. Sans l'indicateur global, vous obtenez seulement un seul match, tous les matchs de changement
{3,10}
à{1,5}
et vous obtenez le résultat "monsieur" à la place.produit
['test', 'test', 'lorem', 'ipsum', 'sir', 'doloret', 'etc', 'etc', 'something']
invalid:test-lorem-ipsum-sir-doloret:end
en plus de la validation des chaînes similaires de moins de 3 articles et plus de 10 articles et de programmation pour la mise en correspondance des processusLorsque vous combinez:
Il peut être déduit qu'il ne peut pas être fait.
Mise à jour: Il y a quelques regex moteurs pour lesquels p. 1 n'est pas nécessairement vrai. Dans ce cas, la regex que vous avez indiqué
start:(?:(\w+)-?){3,10}:end
va faire le travail (source).\G
.preg_match_all
, ce qui le rend très simple et regex seule solution. La limite de capture est peut-être possible avec look-ahead (dans la solution avec\G
). (Je ne prétends pas que cela fonctionne pour tous les cas, il y a, mais il y a une catégorie de cas que cela fonctionne).