¿Cómo combinar, pero no capturar, parte de una expresión regular?

210

Tengo una lista de cadenas. Algunos de ellos son de la forma 123-...456. La porción variable "..." puede ser:

  • la cadena "manzana" seguida de un guión, por ej. 123-apple-456
  • la cadena "banana" seguida de un guión, p. ej. 123-banana-456
  • una cadena en blanco, por ejemplo 123-456(tenga en cuenta que solo hay un guión)

Cualquier palabra que no sea "manzana" o "plátano" no es válida.

Para estos tres casos, me gustaría hacer coincidir "manzana", "plátano" y "", respectivamente. Tenga en cuenta que nunca quiero capturar el guión, pero siempre quiero hacer coincidirlo . Si la cadena no tiene la forma123-...456 descrita anteriormente, entonces no hay ninguna coincidencia.

¿Cómo escribo una expresión regular para hacer esto? Suponga que tengo un sabor que permite mirar hacia adelante, mirar hacia atrás, mirar hacia atrás y grupos que no capturan.


La observación clave aquí es que cuando tienes "manzana" o "plátano", también debes tener el guión final, pero no quieres que coincida. Y cuando coincida con la cadena en blanco, no debe tener el guión final. Una expresión regular que encapsula esta afirmación será la correcta, creo.

David Stone
fuente
¿Quieres unir todo excepto guiones?
BrunoLM

Respuestas:

286

La única forma de no capturar algo es mediante afirmaciones de mirar alrededor :

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

Porque incluso con grupos que no capturan,(?:…) toda la expresión regular captura sus contenidos coincidentes. Pero esta expresión regular solo coincide appleo bananasi está precedida 123-y seguida por -456, o coincide con la cadena vacía si está precedida 123-y 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|
-------------------------------------------------------------------------
Gumbo
fuente
1
+1: en este caso, puede solucionarlo utilizando el grupo 1 en lugar del grupo 0, pero esta es una distinción excelente (¡y sutil!).
Ben Blank
@Ben Blank: Definitivamente depende de cómo se interpreten "match" y "capture".
Gumbo
8
No es compatible con JavaScript, yay ! sería bueno tener un método amigable con JS, pero no está nada mal, +0.5 (redondeando; D)
GiantCowFilms
¡Me encantan las afirmaciones de mirar alrededor! Estos funcionan muy bien con Ruby también.
Rots
solución perfecta, me encanta esto
Trần Quang Hiệp
15

Actualización: ¡Gracias a Germán Rodríguez Herrera!

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

Recuerda que el resultado está en el grupo 1

Demo de Debuggex

op1ekun
fuente
8

Tratar:

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

Eso coincidirá apple, bananao una cadena en blanco, y después de esto habrá un guión 0 o 1. Me equivoqué al no tener la necesidad de un grupo de captura. Tonto de mí.

Thomas
fuente
Esto no es correcto ya que coincide, por ejemplo, "123-coco-456".
David Stone
Pensé que lo querías más general ... arreglado.
Thomas
5

He modificado una de las respuestas (por @ op1ekun):

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

La razón es que la respuesta de @ op1ekun también coincide "123-apple456", sin el guión después de la manzana.

Germán Rodríguez Herrera
fuente
3

Prueba esto:

/\d{3}-(?:(apple|banana)-)?\d{3}/
slosd
fuente
1
Esto no es correcto ya que coincide, por ejemplo, "123-coco-456".
David Stone el
@david: ¿en qué se diferencia eso de tu ejemplo "banana"?
SilentGhost
@SilentGhost: Yo sólo se desea capturar appleo bananao "". Todos los demás valores no son válidos, como dije.
David Stone el
sry, en ese caso: / \ d {3} - (? :( apple | banana) -)? \ d {3} /
slosd el
1
Lo que muestra este ejemplo es que es posible tener un grupo no capturador sin usar lookahead y lookbehind.
Vince Panuccio
0

Una variación de la expresión de @Gumbo que se utiliza \Kpara restablecer las posiciones de coincidencia para evitar la inclusión de bloques de números en la coincidencia. Utilizable en sabores regex PCRE.

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

Partidos:

Match 1  apple
Match 2  banana
Match 3
Oriberu
fuente
-3

De lejos, el más simple (funciona para python) es '123-(apple|banana)-?456'.

johmsp
fuente
1
Esto coincidiría, 123-apple456por lo que no es correcto.
Loren