Regex exactement n OU m fois

105

Considérez l'expression régulière suivante, où Xest toute regex.

X{n}|X{m}

Cette expression régulière testerait pour Xse produire exactement n ou à des mmoments.

Existe-t-il un quantificateur regex qui peut tester une occurrence Xexactement nou des mheures?

FThompson
la source
Le numéro deux occurences de Xest le meilleur que vous pouvez obtenir pour général m, n.
John Dvorak
Si c'était mon problème, j'essaierais des références regex et je commencerais par (X)\1{n-1}(?:\1{m-n-1}). Je sais que cela correspond Xau moins une fois, mais juste pour commencer, essayez cette chose simple, puis affinez en utilisant des lookaheads ou des lookbehinds au lieu de (X).
nalply le

Réponses:

91

Il n'y a pas de quantificateur unique qui signifie «exactement m ou n fois». La façon dont vous le faites est très bien.

Une alternative est:

X{m}(X{k})?

m < net kest la valeur de n-m.

Mark Byers
la source
67

Voici la liste complète des quantificateurs (réf. Http://www.regular-expressions.info/reference.html ):

  • ?, ??- 0 ou 1 occurrence ( ??est paresseux, ?est gourmand)
  • *, *?- n'importe quel nombre d'occurrences
  • +, +?- au moins une occurrence
  • {n}- exactement des noccurrences
  • {n,m}- naux moccurrences, inclusivement
  • {n,m}?- naux mévénements, paresseux
  • {n,}, {n,}?- au moins noccurrence

Pour obtenir "exactement N ou M", vous devez écrire deux fois l'expression rationnelle quantifiée, sauf si m, n sont spéciaux:

  • X{n,m} si m = n+1
  • (?:X{n}){1,2} si m = 2n
  • ...
John Dvorak
la source
1
Pourquoi est-ce ?:nécessaire dans l' m = 2nexemple if ? Semble bien fonctionner sans cela pour moi.
erb
7
@erb si vous omettez ?:, le groupe devient un groupe de capture. Mis à part le moteur de regex se souvenant des choses qu'il n'a pas à faire, si vous avez capturé des groupes après celui-ci, leurs identifiants changeront. Si vous utilisez votre regex pour la substitution, vous devrez ajuster le remplacement.
John Dvorak
3

TLDR; (?<=[^x]|^)(x{n}|x{m})(?:[^x]|$)

On dirait que vous voulez "xn fois" ou "xm fois", je pense qu'une traduction littérale en regex serait (x{n}|x{m}). comme ça https://regex101.com/r/vH7yL5/1

ou, dans le cas où vous pouvez avoir une séquence de plus de m "x" s (en supposant que m> n), vous pouvez ajouter 'après pas de "x"' et 'suivi par pas de "x", en se traduisant par [^x](x{n}|x{m})[^x]mais ce serait Supposons qu'il y ait toujours un caractère derrière et après vos "x". Comme vous pouvez le voir ici: https://regex101.com/r/bB2vH2/1

vous pouvez le changer en (?:[^x]|^)(x{n}|x{m})(?:[^x]|$), en se traduisant par "suivant pas de 'x' ou suivant début de ligne" et "suivi de pas de 'x' ou suivi de fin de ligne". Mais quand même, cela ne correspondra pas à deux séquences avec un seul caractère entre elles (car la première correspondance nécessiterait un caractère après et la seconde un caractère avant) comme vous pouvez le voir ici: https://regex101.com/r/ oC5oJ4 / 1

Enfin, pour correspondre à la correspondance distante à un caractère, vous pouvez ajouter un regard positif vers l'avant (? =) Sur le "non 'x' après" ou un regard positif derrière (? <=) Sur le "non 'x' avant", comme ceci: https://regex101.com/r/mC4uX3/1

(?<=[^x]|^)(x{n}|x{m})(?:[^x]|$)

De cette façon, vous n'obtiendrez que le nombre exact de 'x que vous voulez.

Enhardi
la source
1

Jetant un coup d'œil à la réponse d'Enhardened, ils déclarent que leur avant-dernière expression ne correspondra pas aux séquences avec un seul caractère entre elles. Il existe un moyen simple de résoudre ce problème sans utiliser de regarder en avant / en arrière, et c'est de remplacer le caractère de début / fin par le caractère de limite. Cela vous permet de faire correspondre les limites de mots qui incluent le début / la fin. En tant que tel, l'expression appropriée devrait être:

(?:[^x]|\b)(x{n}|x{m})(?:[^x]|\b)

Comme vous pouvez le voir ici: https://regex101.com/r/oC5oJ4/2 .

rozza2058
la source
1
Cool, je n'étais pas familier avec la façon dont les regex géraient les limites. Le seul problème avec cette méthode est lorsque vous utilisez une limite non standard. Tale a look: regex101.com/r/j0nkeo/1 et regex101.com/r/4Ix7Dr/1
Enhardened
1
@Enhardened - c'est un bon point, semble être un problème avec plusieurs groupes correspondants qui se chevauchent. C'est une situation où vous devez utiliser le regard en arrière.
rozza2058
1

Article très ancien, mais j'aimerais contribuer à ce qui pourrait être utile. Je l'ai essayé exactement de la manière indiquée dans la question et cela fonctionne, mais il y a un hic: l'ordre des quantités compte. Considère ceci:

#[a-f0-9]{6}|#[a-f0-9]{3}

Cela trouvera toutes les occurrences de codes de couleur hexadécimaux (ils comportent 3 ou 6 chiffres). Mais quand je le retourne comme ça

#[a-f0-9]{3}|#[a-f0-9]{6}

il ne trouvera que les 3 chiffres ou les 3 premiers chiffres des 6 chiffres. Cela a du sens et un pro Regex pourrait le repérer tout de suite, mais pour beaucoup, cela pourrait être un comportement particulier. Il existe des fonctionnalités Regex avancées qui pourraient éviter ce piège quel que soit l'ordre, mais tout le monde n'est pas plongé dans les modèles Regex.

DanDan
la source