Como combinar, mas não capturar, parte de uma regex?

209

Eu tenho uma lista de strings. Alguns deles são da forma 123-...456. A porção variável "..." pode ser:

  • a string "apple" seguida de um hífen, por exemplo 123-apple-456
  • a cadeia "banana" seguida de um hífen, por exemplo 123-banana-456
  • uma string em branco, por exemplo 123-456(observe que há apenas um hífen)

Qualquer palavra que não seja "maçã" ou "banana" é inválida.

Para esses três casos, eu gostaria de combinar "maçã", "banana" e "", respectivamente. Observe que eu nunca quero capturar o hífen, mas sempre quero combiná- lo. Se a sequência não tiver a forma 123-...456descrita acima, não haverá correspondência.

Como escrevo uma expressão regular para fazer isso? Suponha que eu tenha um sabor que permita grupos de olhar para trás, olhar para trás, olhar em volta e não capturar.


A observação principal aqui é que, quando você tem "maçã" ou "banana", também deve ter o hífen à direita, mas não deseja correspondê-lo. E quando você estiver combinando a sequência em branco, não deverá ter o hífen à direita. Um regex que encapsula essa afirmação será o correto, eu acho.

David Stone
fonte
Você quer combinar tudo, exceto hífens?
BrunoLM

Respostas:

286

A única maneira de não capturar algo é usando asserções :

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

Porque, mesmo em grupos que não capturam,(?:…) a expressão regular inteira captura o conteúdo correspondente. Mas essa expressão regular corresponde apenas appleou bananase é precedida por 123-e seguida por -456, ou corresponde à string vazia se for precedida por 123-e seguida por 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|
-------------------------------------------------------------------------
quiabo
fonte
1
+1 - Nesse caso, você pode contornar isso usando o grupo 1 em vez do grupo 0, mas essa é uma distinção excelente (e sutil!).
Ben Blank
@ Ben Blank: Definitivamente depende de como "match" e "capture" são interpretados.
Gumbo
8
Não suportado em JavaScript, yay ! Seria bom ter um método amigável JS, mas não é mau de todo, 0,5 (arredondamento; D)
GiantCowFilms
Adoro declarações gerais! Eles também funcionam muito bem com Ruby.
Rots
solução perfeita, eu adoro isso
Tr Qun Quang Hiệp
15

Atualização: Obrigado a Germán Rodríguez Herrera!

Em javascript tente: /123-(apple(?=-)|banana(?=-)|(?!-))-?456/

Lembre-se de que o resultado está no grupo 1

Demo de depuração

op1ekun
fonte
8

Experimentar:

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

Isso corresponderá a apple, bananaou uma sequência em branco e, a seguir, haverá um 0 ou 1 hífen. Eu estava errado em não ter necessidade de um grupo de captura. Parvo eu.

Thomas
fonte
Isso não está correto, pois corresponde, por exemplo, "123-coconut-456".
David Stone
Achei que você queria mais geral ... fixo.
Thomas
5

Modifiquei uma das respostas (por @ op1ekun):

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

O motivo é que a resposta de @ op1ekun também corresponde "123-apple456", sem o hífen após a maçã.

Germán Rodríguez Herrera
fonte
3

Tente o seguinte:

/\d{3}-(?:(apple|banana)-)?\d{3}/
slosd
fonte
1
Isso não está correto, pois corresponde, por exemplo, "123-coconut-456".
David Stone
@ David: como isso é diferente do seu exemplo de "banana"?
SilentGhost 13/10/10
@SilentGhost: I única deseja capturar appleou bananaou "". Todos os outros valores são inválidos, como afirmei.
David Stone
sry, nesse caso: / \ d {3} - (? :( maçã | banana) -)? \ d {3} /
slosd
1
O que este exemplo mostra é que é possível ter um grupo sem captura sem usar lookahead e lookbehind.
Vince Panuccio
0

Uma variação da expressão do @Gumbo que é usada \Kpara redefinir as posições de correspondência para impedir a inclusão de blocos numéricos na correspondência. Utilizável nos sabores PCRE regex.

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

Partidas:

Match 1  apple
Match 2  banana
Match 3
oriberu
fonte
-3

De longe, o mais simples (funciona para python) é '123-(apple|banana)-?456'.

johmsp
fonte
1
Isso corresponderia, 123-apple456portanto, não está correto.
Loren