Expresiones regulares: ¿hay un operador AND?

708

Obviamente, puede usar |(¿tubería?) Para representar OR, pero ¿hay alguna manera de representar ANDtambién?

Específicamente, me gustaría hacer coincidir los párrafos de texto que contienen TODOS de una frase determinada, pero sin ningún orden en particular.

Hugoware
fuente
1
¿Quiere decir que desea encontrar frases en un texto, donde cada frase es una permutación válida de las palabras en una frase dada?
Nietzche-jou
2
Estoy poniendo esto aquí porque tres o cuatro respuestas lo ignoran. Lookahead no coincide con la misma longitud para cada cláusula, a menos que terminen en $. Una búsqueda anticipada podría coincidir con cuatro caracteres y otros 6. Por ejemplo, (? = A *) (? = Aab) coincidirá con aabaaaaba
Zachary Vance
2
intente usar solo el carácter "espacio" para el operador "Y".
1 I'd like to match paragraphs of text.. 2. Contiene texto fuera de orden . El número 1 está abierto a interpretación. El número 2 se puede hacer de dos maneras. Modo 1: (?:(?:(?(1)(?!))\b(phrase1)\b.*?|(?(2)(?!))\b(phrase2)\b.*?)){2}Modo 2: (?=.*\bphrase1\b)(?=.*\bphrase2\b)en este caso, la coincidencia del párrafo en este caso no está definida hasta que se formalice la definición del párrafo.

Respuestas:

385

Use una expresión regular que no consuma.

La notación típica (es decir, Perl / Java) es:

(?=expr)

Esto significa "hacer coincidir expr pero luego continuar haciendo coincidir en el punto de partida original".

Puede hacer tantos como desee, y esto será un "y". Ejemplo:

(?=match this expression)(?=match this too)(?=oh, and this)

Incluso puede agregar grupos de captura dentro de las expresiones no consumidoras si necesita guardar algunos de los datos allí.

Jason Cohen
fuente
3
perl -e "q {algunas cosas y cosas} = ~ / (? = algunas) (? = cosas) (? = cosas) /? print 'yes': print 'no'" imprime 'no'.
Robert P
27
Cabe mencionar que este ejemplo en particular se llama afirmación anticipada positiva. Tiene otros usos que "y". Tenga en cuenta que el texto no se consume.
extraño
77
El uso de (? =) Como este da como resultado una expresión regular que nunca puede tener éxito. Pero es la conjunción análoga a |. El OP simplemente está equivocado en lo que cree que resolverá su problema.
Nietzche-jou el
10
perl -e "q {algunas cosas y cosas} = ~ /(?=.*some)(?=.*stuff)(?=.*things)/? print 'yes': print 'no'"
kriss
3
¿Puede agregar algún ejemplo fácil en el código perl en su respuesta?
Pithikos
343

Debe usar anticipación como han dicho algunos de los otros respondedores, pero la anticipación debe tener en cuenta otros caracteres entre su palabra objetivo y la posición de coincidencia actual. Por ejemplo:

(?=.*word1)(?=.*word2)(?=.*word3)

El .*primer vistazo anticipado le permite coincidir con la cantidad de caracteres que necesita antes de llegar a "palabra1". Luego se restablece la posición de coincidencia y la segunda búsqueda anticipada busca "word2". Restablezca de nuevo, y la parte final coincide con "word3"; Dado que es la última palabra que está buscando, no es necesario que esté en un futuro, pero no duele.

Para hacer coincidir un párrafo completo, debe anclar la expresión regular en ambos extremos y agregar una final .*para consumir los caracteres restantes. Usando la notación de estilo Perl, eso sería:

/^(?=.*word1)(?=.*word2)(?=.*word3).*$/m

El modificador 'm' es para el modo multilínea; permite ^y $coincide en los límites de los párrafos ("límites de línea" en expresiones regulares). Es esencial en este caso que no utilice el modificador 's', que permite que el metacarácter de punto coincida con las nuevas líneas y con todos los demás caracteres.

Finalmente, desea asegurarse de que está haciendo coincidir palabras enteras y no solo fragmentos de palabras más largas, por lo que debe agregar límites de palabras:

/^(?=.*\bword1\b)(?=.*\bword2\b)(?=.*\bword3\b).*$/m
Alan Moore
fuente
8
Exactamente correcto, ¡también hay un tutorial sobre esto! ocpsoft.org/tutorials/regular-expressions/and-in-regex
Lincoln
99
Muchas gracias. * Esto marca la diferencia
Gennadiy Ryabkin
1
+1 para obtener una respuesta clara y sucinta que muestre uno de los mejores usos de lookaheads (a diferencia de los usos como un truco para contar el porcentaje de coincidencia de una contraseña). :)
zx81
1
@Liam :. MySQL usa el sabor POSIX ERE, así que no. Efectivamente sacrifica características a favor del rendimiento, lo que me parece razonable. Hay más información aquí .
Alan Moore
3
reemplace .*con [\s\S]*javascript si tiene nuevas líneas, ya que .el motor regex de javascript no coincide con las nuevas líneas y no se puede hacer que se modifiquen
Wesley Smith
41

Mira este ejemplo:

Tenemos 2 expresiones regulares A y B y queremos unir ambas, por lo que en pseudocódigo se ve así:

pattern = "/A AND B/"

Se puede escribir sin usar el operador AND de esta manera:

pattern = "/NOT (NOT A OR NOT B)/"

en PCRE:

"/(^(^A|^B))/"

regexp_match(pattern,data)
fanjabi
fuente
24
Eso es cierto en términos de lógica formal, pero no es de ninguna ayuda aquí. En expresiones regulares, NO puede ser aún más difícil de expresar que AND.
Alan Moore
@marvin_dpr Me funcionó en CMake mientras que la otra sugerencia (?=expr)no. Parece ser dependiente de la implementación.
Melebius
38
¿No ^significa "comienzo de cadena" en la sintaxis de expresiones regulares?
Lambda Fairy
3
En regex en general, ^es negación solo al comienzo de una clase de personaje. A menos que CMake esté haciendo algo realmente extraño (hasta el punto de que llamar a su lenguaje de coincidencia de patrones "regex" podría considerarse engañoso o incorrecto) supongo que el hecho de que funcionó para usted fue un accidente aislado.
tripleee
29

Puede hacerlo con una expresión regular, pero probablemente querrá otra. Por ejemplo, use varias expresiones regulares y combínelas en una cláusula if.

Puede enumerar todas las permutaciones posibles con una expresión regular estándar, como esta (coincide con a, byc en cualquier orden):

(abc)|(bca)|(acb)|(bac)|(cab)|(cba)

Sin embargo, esto hace una expresión regular muy larga y probablemente ineficiente, si tiene más de un par de términos.

Si está utilizando una versión extendida de expresiones regulares, como Perl o Java, tienen mejores formas de hacerlo. Otras respuestas han sugerido el uso de operaciones positivas de búsqueda anticipada.

Juha Syrjälä
fuente
10
No creo que su enfoque sea más ineficiente que 3 lookaheads con su retroceso catastrófico. Claro que es más largo escribir, pero tenga en cuenta que puede generar fácilmente el patrón automáticamente. Tenga en cuenta que puede mejorarlo para que falle más rápido a(bc|cb)|b(ac|ca)|c(ab|ba). Y lo más importante, puedes usarlo con todo el sabor regex.
Casimir et Hippolyte
27

El operador AND está implícito en la sintaxis RegExp.
En cambio, el operador OR debe especificarse con una tubería.
El siguiente RegExp:

var re = /ab/;

significa la letra a Y la letra b.
También funciona con grupos:

var re = /(co)(de)/;

significa el grupo co Y el grupo de.
Reemplazar el AND (implícito) con un OR requeriría las siguientes líneas:

var re = /a|b/;
var re = /(co)|(de)/;
Emanuele Del Grande
fuente
29
Desafortunadamente, esto no es lo que solicitó el OP. Esto encuentra cualquier cosa en ese orden, mientras que los querían en cualquier orden. Vea la respuesta en stackoverflow.com/users/20938/alan-moore a continuación, cuál es la correcta.
JESii
1
@JESii gracias por tu comentario, tienes razón y entendí mal la pregunta de Hugoware, me concentré particularmente en su primera oración. La respuesta correcta es un uso adecuado del operador de búsqueda anticipada, como escribió AlanMoore. De todos modos, creo que alguien puede encontrar útil mi aclaración, como ya se ha votado, por lo que no tiraría todo. Saludos.
Emanuele Del Grande
13

¿No es posible en su caso hacer el AND en varios resultados coincidentes? en seudocódigo

regexp_match(pattern1, data) && regexp_match(pattern2, data) && ...
usuario54579
fuente
3
Estoy en una situación en la que tengo un código que es una tabla de reglas de datos, con una sola cadena de coincidencia de patrón regex para probar la validez de la regla. ¡Pasar a varias pruebas no es algo que pueda hacer en mi caso, y comúnmente en los casos de otras personas también!
Alan Wolfe
11

¿Por qué no usar awk?
con awk regex AND, OR importa es tan simple

awk '/WORD1/ && /WORD2/ && /WORD3/' myfile
mug896
fuente
9

Si usa expresiones regulares de Perl, puede usar anticipación positiva:

Por ejemplo

(?=[1-9][0-9]{2})[0-9]*[05]\b

serían números mayores que 100 y divisibles por 5

jpalecek
fuente
8

Podría canalizar su salida a otra expresión regular. Usando grep, podrías hacer esto:

grep A | grep B

recolector de basura
fuente
8

Además de la respuesta aceptada

Le proporcionaré algunos ejemplos prácticos que aclararán las cosas para algunos de ustedes. Por ejemplo, digamos que tenemos esas tres líneas de texto:

[12/Oct/2015:00:37:29 +0200] // only this + will get selected
[12/Oct/2015:00:37:x9 +0200]
[12/Oct/2015:00:37:29 +020x]

Ver demo aquí DEMO

Lo que queremos hacer aquí es seleccionar el signo + pero solo si es después de dos números con un espacio y si es antes de cuatro números. Esas son las únicas limitaciones. Usaríamos esta expresión regular para lograrlo:

'~(?<=\d{2} )\+(?=\d{4})~g'

Tenga en cuenta que si separa la expresión, obtendrá resultados diferentes.

O tal vez desee seleccionar texto entre las etiquetas ... ¡pero no las etiquetas! Entonces podrías usar:

'~(?<=<p>).*?(?=<\/p>)~g'

para este texto:

<p>Hello !</p> <p>I wont select tags! Only text with in</p> 

Ver demo aquí DEMO

DevWL
fuente
¿Cuál fue la respuesta aceptada? Por favor agregue un enlace para el futuro yo.
James Brown
6

El orden siempre está implícito en la estructura de la expresión regular. Para lograr lo que desea, deberá hacer coincidir la cadena de entrada varias veces con diferentes expresiones.

Lo que quieres hacer no es posible con una sola expresión regular.

Pilif
fuente
No es técnicamente imposible, pero no vale la pena implementarlo. No sé por qué alguien votó en contra ...
Robert P
13
Probablemente porque no solo es posible, es simple, suponiendo que su sabor regex sea compatible con lookaheads. Y esa es una buena apuesta; la mayoría de los principales lenguajes de programación actuales los admiten.
Alan Moore
3

Use AND fuera de la expresión regular. En PHP, el operador de búsqueda anticipada no parecía funcionar para mí, sino que usé esto

if( preg_match("/^.{3,}$/",$pass1) && !preg_match("/\s{1}/",$pass1))
    return true;
else
    return false;

La expresión regular anterior coincidirá si la longitud de la contraseña es de 3 caracteres o más y no hay espacios en la contraseña.

Hammad Khan
fuente