Как сопоставить, но не захватить, часть регулярного выражения?

210

У меня есть список строк. Некоторые из них имеют форму 123-...456. Переменная часть "..." может быть:

  • строка «яблоко», за которой следует дефис, например 123-apple-456
  • строка «банан» с последующим дефисом, например 123-banana-456
  • пустая строка, например 123-456(обратите внимание, что есть только один дефис)

Любое слово, кроме «яблоко» или «банан», является недействительным.

Для этих трех случаев я бы хотел сопоставить «яблоко», «банан» и «» соответственно. Обратите внимание, что я никогда не хочу захватывать дефис, но я всегда хочу соответствовать ему. Если строка не имеет форму, 123-...456описанную выше, тогда совпадения не будет.

Как мне написать регулярное выражение для этого? Предположим, у меня есть аромат, который позволяет группам «смотреть вперед», «смотреть назад», «смотреть вокруг» и «не захватывать».


Ключевое наблюдение здесь заключается в том, что когда у вас есть «яблоко» или «банан», у вас также должен быть конечный дефис, но вы не хотите, чтобы он соответствовал. И когда вы сопоставляете пустую строку, у вас не должно быть завершающего дефиса. Я думаю, правильное выражение, которое заключает в себе это утверждение, будет правильным.

Дэвид Стоун
источник
Вы хотите сопоставить все, кроме дефисов?
BrunoLM

Ответы:

286

Единственный способ не запечатлеть что-либо - использование проверочных утверждений :

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

Потому что даже с не захватывающими группами(?:…) все регулярное выражение захватывает их совпадающее содержимое. Но это регулярное выражение соответствует только appleили bananaесли ему предшествует 123-и следует -456, или оно соответствует пустой строке, если ему предшествует 123-и сопровождается 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|
-------------------------------------------------------------------------
гумбо
источник
1
+1 - В этом случае вы можете обойти это, используя группу 1, а не группу 0, но это отличное (и тонкое!) Различие.
Бен Бланк
@Ben Blank: Это определенно зависит от того, как интерпретируются «совпадение» и «захват».
Гамбо
8
Не поддерживается в JavaScript, ура ! было бы неплохо иметь JS-дружественный метод, но совсем не плохо, +0,5 (округление вверх; D)
GiantCowFilms
Люблю осмотрительные утверждения! Они прекрасно работают и с Ruby.
гниет
идеальное решение, я люблю это
Trần Quang Hiệp
15

Обновление: благодаря Херману Родригесу Эррере!

В javascript попробуйте: /123-(apple(?=-)|banana(?=-)|(?!-))-?456/

Помните, что результат в группе 1

Debuggex Demo

op1ekun
источник
8

Пытаться:

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

Это будет соответствовать apple, bananaили пустая строка, и после нее будет 0 или 1 дефис. Я ошибался из-за того, что не нуждался в группе захвата. Я такой глупый.

Томас
источник
Это неверно, так как оно соответствует, например, «123-кокос-456».
Дэвид Стоун
Думал, ты хотел, чтобы это было более общим ... исправлено.
Томас
5

Я изменил один из ответов (автор @ op1ekun):

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

Причина в том, что ответ @ op1ekun также совпадает "123-apple456", без дефиса после яблока.

Херман Родригес Эррера
источник
3

Попробуй это:

/\d{3}-(?:(apple|banana)-)?\d{3}/
slosd
источник
1
Это неверно, так как оно соответствует, например, «123-кокос-456».
Дэвид Стоун
@ Давид: как это отличается от вашего "бананового" примера?
SilentGhost
@SilentGhost: я хочу только захватить appleили bananaили "". Все остальные значения недействительны, как я уже говорил.
Дэвид Стоун
sry, в этом случае: / \ d {3} - (? :( apple | banana) -)? \ d {3} /
slosd
1
Этот пример показывает, что можно создать группу без захвата, не используя lookahead и lookbehind.
Винс Пануччо
0

Вариант выражения @Gumbo, который используется \Kдля сброса позиций совпадений, чтобы предотвратить включение блоков чисел в совпадение. Используется в PCRE регулярных выражениях.

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

Спички:

Match 1  apple
Match 2  banana
Match 3
oriberu
источник
-3

Безусловно самое простое (работает для python) '123-(apple|banana)-?456'.

johmsp
источник
1
Это будет соответствовать, 123-apple456так что это не правильно.
Лорен