Comment faire correspondre, mais pas capturer, une partie d'une expression régulière?

210

J'ai une liste de chaînes. Certains d'entre eux sont de la forme 123-...456. La partie variable "..." peut être:

  • la chaîne "pomme" suivie d'un trait d'union, par exemple 123-apple-456
  • la chaîne "banana" suivie d'un trait d'union, par exemple 123-banana-456
  • une chaîne vide, par exemple 123-456(notez qu'il n'y a qu'un seul tiret)

Tout mot autre que "pomme" ou "banane" n'est pas valide.

Pour ces trois cas, je voudrais faire correspondre respectivement "pomme", "banane" et "". Notez que je ne veux jamais capturer le trait d'union, mais je veux toujours le faire correspondre . Si la chaîne n'est pas de la forme 123-...456décrite ci-dessus, il n'y a aucune correspondance.

Comment puis-je écrire une expression régulière pour ce faire? Supposons que j'ai une saveur qui autorise les groupes d'anticipation, de recherche, de recherche et de non capture.


L'observation clé ici est que lorsque vous avez "pomme" ou "banane", vous devez également avoir le trait d'union, mais vous ne voulez pas le faire correspondre. Et lorsque vous faites correspondre la chaîne vide, vous ne devez pas avoir le trait d'union de fin. Une expression régulière qui résume cette affirmation sera la bonne, je pense.

David Stone
la source
Vous voulez tout faire correspondre sauf les tirets?
BrunoLM

Réponses:

286

La seule façon de ne pas capturer quelque chose consiste à utiliser des assertions indirectes :

(?<=123-)((apple|banana)(?=-456)|(?=456))

Parce que même avec des groupes non capturants,(?:…) l'expression régulière entière capture leur contenu correspondant. Mais cette expression régulière ne correspond qu'à appleou bananasi elle est précédée 123-et suivie de -456, ou si elle correspond à la chaîne vide si elle est précédée 123-et suivie de 456.

|Lookaround  |    Name      |        What it Does                       |
-----------------------------------------------------------------------
|(?=foo)     |   Lookahead  | Asserts that what immediately FOLLOWS the |
|            |              |  current position in the string is foo    |
-------------------------------------------------------------------------
|(?<=foo)    |   Lookbehind | Asserts that what immediately PRECEDES the|
|            |              |  current position in the string is foo    |
-------------------------------------------------------------------------
|(?!foo)     |   Negative   | Asserts that what immediately FOLLOWS the |
|            |   Lookahead  |  current position in the string is NOT foo|
-------------------------------------------------------------------------
|(?<!foo)    |   Negative   | Asserts that what immediately PRECEDES the|
|            |   Lookbehind |  current position in the string is NOT foo|
-------------------------------------------------------------------------
Gombo
la source
1
+1 - Dans ce cas, vous pouvez contourner cela en utilisant le groupe 1 plutôt que le groupe 0, mais c'est une excellente distinction (et subtile!).
Ben Blank
@Ben Blank: Cela dépend certainement de la façon dont «match» et «capture» sont interprétés.
Gumbo
8
Non pris en charge en JavaScript, yay ! serait bien d'avoir une méthode conviviale JS, mais pas mal du tout, +0,5 (arrondi; D)
GiantCowFilms
Aimez les assertions de regard! Cela fonctionne aussi très bien avec Ruby.
Rots
solution parfaite, j'adore ça
Trần Quang Hiệp
15

Mise à jour: Merci à Germán Rodríguez Herrera!

En javascript, essayez: /123-(apple(?=-)|banana(?=-)|(?!-))-?456/

N'oubliez pas que le résultat est dans le groupe 1

Démo Debuggex

op1ekun
la source
8

Essayer:

123-(?:(apple|banana|)-|)456

Cela correspond apple, bananaou une chaîne vide, et après il y aura un 0 ou 1 des traits d' union. J'avais tort de ne pas avoir besoin d'un groupe de capture. Que je suis bête.

Thomas
la source
Ce n'est pas correct car il correspond, par exemple, "123-coconut-456".
David Stone
Je pensais que tu le voulais plus général ... fixe.
Thomas
5

J'ai modifié l'une des réponses (par @ op1ekun):

123-(apple(?=-)|banana(?=-)|(?!-))-?456

La raison en est que la réponse de @ op1ekun correspond également "123-apple456", sans le trait d'union après apple.

Germán Rodríguez Herrera
la source
3

Essaye ça:

/\d{3}-(?:(apple|banana)-)?\d{3}/
slosd
la source
1
Ce n'est pas correct car il correspond, par exemple, "123-coconut-456".
David Stone
@david: en quoi est-ce différent de votre exemple "banane"?
SilentGhost
@SilentGhost: Je veux seulement capturer appleou bananaou "". Toutes les autres valeurs sont invalides, comme je l'ai indiqué.
David Stone
sry, dans ce cas: / \ d {3} - (? :( apple | banana) -)? \ d {3} /
slosd
1
Ce que cet exemple montre, c'est qu'il est possible d'avoir un groupe non capturant sans utiliser lookahead et lookbehind.
Vince Panuccio
0

Variation de l'expression de @Gumbo qui utilise \Kpour réinitialiser les positions de correspondance afin d'empêcher l'inclusion de blocs de numéros dans la correspondance. Utilisable dans les saveurs regex PCRE.

123-\K(?:(?:apple|banana)(?=-456)|456\K)

Allumettes:

Match 1  apple
Match 2  banana
Match 3
oriberu
la source
-3

De loin le plus simple (fonctionne pour python) est '123-(apple|banana)-?456'.

johmsp
la source
1
Cela correspondrait 123-apple456donc ce n'est pas correct.
Loren