Sé que es posible hacer coincidir una palabra y luego revertir las coincidencias utilizando otras herramientas (por ejemplo grep -v
). Sin embargo, ¿es posible hacer coincidir líneas que no contengan una palabra específica, por ejemplo hede
, usando una expresión regular?
Entrada:
hoho
hihi
haha
hede
Código:
grep "<Regex for 'doesn't contain hede'>" input
Salida deseada:
hoho
hihi
haha
regex
regex-negation
knaser
fuente
fuente
([^h]*(h([^e]|$)|he([^d]|$)|hed([^e]|$)))*
:? La idea es simple. Siga haciendo coincidir hasta que vea el inicio de la cadena no deseada, luego solo haga coincidir en los casos N-1 donde la cadena está sin terminar (donde N es la longitud de la cadena). Estos casos N-1 son "h seguido de no e", "seguido de no d" y "hed seguido de no e". Si logró pasar estos casos N-1, no coincidió con la cadena no deseada para que pueda comenzar a buscar de[^h]*
nuevo^([^h]*(h([^e]|$)|he([^d]|$)|hed([^e]|$))?)*$
esto falla cuando las instancias de "hede" están precedidas por instancias parciales de "hede", como en "hhede".Respuestas:
La noción de que regex no admite la coincidencia inversa no es del todo cierto. Puede imitar este comportamiento utilizando miradas negativas:
La expresión regular anterior coincidirá con cualquier cadena o línea sin un salto de línea, que no contenga la (sub) cadena 'hede'. Como se mencionó, esto no es algo que regex sea "bueno" (o debería hacer), pero aún así, es posible.
Y si necesita hacer coincidir también los caracteres de salto de línea, use el modificador DOT-ALL (el seguimiento
s
en el siguiente patrón):o úsalo en línea:
(donde
/.../
están los delimitadores de expresiones regulares, es decir, no forman parte del patrón)Si el modificador DOT-ALL no está disponible, puede imitar el mismo comportamiento con la clase de caracteres
[\s\S]
:Explicación
Una cadena es solo una lista de
n
caracteres. Antes y después de cada personaje, hay una cadena vacía. Entonces, una lista den
caracteres tendrán+1
cadenas vacías. Considere la cadena"ABhedeCD"
:donde los
e
's son las cadenas vacías. La expresión regular(?!hede).
mira hacia adelante para ver si no se"hede"
puede ver ninguna subcadena , y si ese es el caso (entonces se ve algo más), entonces el.
(punto) coincidirá con cualquier carácter, excepto un salto de línea. Las miradas también se llaman aserciones de ancho cero porque no consumen ningún carácter. Solo afirman / validan algo.Entonces, en mi ejemplo, cada cadena vacía se valida por primera vez para ver si no hay ninguna
"hede"
más adelante, antes de que el carácter.
(punto) consuma un carácter . La expresión regular(?!hede).
hará que sólo una vez, por lo que se envuelve en un grupo, y repetido cero o más veces:((?!hede).)*
. Finalmente, el inicio y el final de la entrada están anclados para garantizar que se consuma toda la entrada:^((?!hede).)*$
Como se puede ver, la entrada
"ABhedeCD"
fallará porque ele3
, la expresión regular(?!hede)
falla (no es"hede"
más adelante!).fuente
grep
que menciona el OP) con soporte de expresiones regulares tienen características que las hacen no regulares en un sentido teórico.^\(\(hede\)\@!.\)*$
Tenga en cuenta que la solución a no comienza con "hede" :
es generalmente mucho más eficiente que la solución para que no contenga "hede" :
El primero verifica "hede" solo en la primera posición de la cadena de entrada, en lugar de en cada posición.
fuente
(.*)(?<!hede)$
. La versión de @Nyerguds también funcionaría, pero pierde por completo el punto de rendimiento que menciona la respuesta.^((?!hede).)*$
? ¿No es más eficiente de usar^(?!.*hede).*$
? Hace lo mismo pero en menos pasosSi solo lo está usando para grep, puede usarlo
grep -v hede
para obtener todas las líneas que no contienen hede.ETA Oh, releyendo la pregunta,
grep -v
es probablemente lo que quisiste decir con "opciones de herramientas".fuente
grep -v -e hede -e hihi -e ...
grep -v "hede\|hihi"
:)grep -vf pattern_file file
egrep
ogrep -Ev "hede|hihi|etc"
para evitar el escape incómodo.Responder:
Explicación:
^
al comienzo de la cadena,(
agrupe y capture a \ 1 (0 o más veces (que coincida con la mayor cantidad posible)),(?!
mire hacia adelante para ver si no hay,hede
tu cuerda)
fin de la anticipación,.
cualquier carácter excepto \ n,)*
fin de \ 1 (Nota: debido a que está utilizando un cuantificador en esta captura, solo la ÚLTIMA repetición del patrón capturado se almacenará en \ 1)$
antes de un \ n opcional, y el final de la cuerdafuente
^((?!DSAU_PW8882WEB2|DSAU_PW8884WEB2|DSAU_PW8884WEB).)*$
'Las respuestas dadas están perfectamente bien, solo un punto académico:
Expresiones regulares en el significado de las ciencias de la computación teórica NO SON CAPAZ de hacerlo así. Para ellos tenía que verse así:
Esto solo hace una coincidencia COMPLETA. Hacerlo para sub-partidos sería incluso más incómodo.
fuente
(hede|Hihi)
'? (Esta quizás sea una pregunta para CS.)Si desea que la prueba de expresión regular solo falle si la cadena completa coincide, lo siguiente funcionará:
por ejemplo: si desea permitir todos los valores excepto "foo" (es decir, "foofoo", "barfoo" y "foobar" pasarán, pero "foo" fallará), use:
^(?!foo$).*
Por supuesto, si está verificando la igualdad exacta , una mejor solución general en este caso es verificar la igualdad de la cadena, es decir
Incluso podría poner la negación fuera de la prueba si necesita características de expresiones regulares (aquí, insensibilidad a mayúsculas y minúsculas):
Sin embargo, la solución de expresiones regulares en la parte superior de esta respuesta puede ser útil en situaciones en las que se requiere una prueba de expresión regular positiva (quizás por una API).
fuente
" hede "
?\s
directiva coincide con un solo personaje de espacio en blanco^(?!\s*hede\s*$).*
FWIW, dado que los lenguajes regulares (también conocidos como lenguajes racionales) están cerrados bajo complementación, siempre es posible encontrar una expresión regular (también conocida como expresión racional) que niega otra expresión. Pero no muchas herramientas implementan esto.
Vcsn admite este operador (que denota
{c}
, postfix).En primer lugar, definir el tipo de sus expresiones: las etiquetas son letra (
lal_char
) para elegira
az
por ejemplo (que define el alfabeto cuando se trabaja con la complementación es, por supuesto, muy importante), y el "valor" calculado para cada palabra es sólo un booleano :true
la palabra es aceptadafalse
, rechazada.En Python:
entonces ingresas tu expresión:
convierte esta expresión a un autómata:
finalmente, convierta este autómata a una expresión simple.
donde
+
generalmente se denota|
,\e
denota la palabra vacía y[^]
generalmente se escribe.
(cualquier carácter). Entonces, con un poco de reescritura()|h(ed?)?|([^h]|h([^e]|e([^d]|d([^e]|e.)))).*
.Puede ver este ejemplo aquí y probar Vcsn en línea allí .
fuente
|
no jugarán bien.'^(()|h(ed?)?|([^h]|h([^e]|e([^d]|d([^e]|e.)))).*)$'
.Aquí hay una buena explicación de por qué no es fácil negar una expresión regular arbitraria. Sin embargo, tengo que estar de acuerdo con las otras respuestas: si esto no es una pregunta hipotética, entonces una expresión regular no es la opción correcta aquí.
fuente
Con anticipación negativa, la expresión regular puede coincidir con algo que no contiene un patrón específico. Esto es respondido y explicado por Bart Kiers. ¡Gran explicación!
Sin embargo, con la respuesta de Bart Kiers, la parte de anticipación pondrá a prueba de 1 a 4 caracteres por delante, mientras que coincide con cualquier carácter individual. Podemos evitar esto y dejar que la parte anticipada revise todo el texto, asegúrese de que no haya 'hede', y luego la parte normal (. *) Puede comer todo el texto al mismo tiempo.
Aquí está la expresión regular mejorada:
Tenga en cuenta que el cuantificador diferido (*?) En la parte de búsqueda anticipada negativa es opcional, puede usar un cuantificador codicioso (*) en su lugar, dependiendo de sus datos: si 'hede' se presenta y en la mitad inicial del texto, el cuantificador diferido puede se más rápido; de lo contrario, el cuantificador codicioso será más rápido. Sin embargo, si 'hede' no se presenta, ambos serían igual de lentos.
Aquí está el código de demostración .
Para obtener más información sobre lookahead, consulte el excelente artículo: Dominar Lookahead y Lookbehind .
Además, consulte RegexGen.js , un generador de expresiones regulares de JavaScript que ayuda a construir expresiones regulares complejas. Con RegexGen.js, puede construir la expresión regular de una manera más legible:
fuente
^(?!.*(str1|str2)).*$
^(?!.*?(?:str1|str2)).*$
, dependiendo de sus datos. Se agregó el?:
ya que no necesitamos capturarlo.Puntos de referencia
Decidí evaluar algunas de las opciones presentadas y comparar su rendimiento, así como utilizar algunas características nuevas. Evaluación comparativa en .NET Regex Engine: http://regexhero.net/tester/
Texto de referencia:
¡Las primeras 7 líneas no deberían coincidir, ya que contienen la Expresión buscada, mientras que las 7 líneas inferiores deberían coincidir!
Resultados:
Los resultados son iteraciones por segundo como la mediana de 3 carreras - Mayor número = mejor
Como .NET no admite verbos de acción (* FAIL, etc.) no pude probar las soluciones P1 y P2.
Resumen:
Intenté probar la mayoría de las soluciones propuestas, algunas optimizaciones son posibles para ciertas palabras. Por ejemplo, si las primeras dos letras de la cadena de búsqueda no son las mismas, la respuesta 03 se puede ampliar para dar como
^(?>[^R]+|R+(?!egex Hero))*$
resultado una pequeña ganancia de rendimiento.Pero la solución general más rápida y legible en términos de rendimiento parece ser 05 usando una declaración condicional o 04 con el cuantificador posesivo. Creo que las soluciones Perl deberían ser aún más rápidas y más fáciles de leer.
fuente
^(?!.*hede)
tomar el tiempo . /// Además, probablemente sea mejor clasificar las expresiones para el corpus coincidente y el corpus no coincidente por separado porque generalmente es un caso en el que la mayoría de las líneas coinciden o la mayoría de las líneas no.No regex, pero he encontrado lógico y útil usar greps en serie con tubería para eliminar el ruido.
p.ej. buscar un archivo de configuración de apache sin todos los comentarios
y
La lógica de los greps en serie es (no es un comentario) y (coincide con dir)
fuente
grep -v
good_stuff #comment_stuff
con esto, evitas probar con anticipación en cada posición:
equivalente a (para .net):
Vieja respuesta:
fuente
/^[^h]*(?:h+(?!ede)[^h]*)*$/
Lo mencionado anteriormente
(?:(?!hede).)*
es excelente porque puede anclarse.Pero lo siguiente sería suficiente en este caso:
Esta simplificación está lista para agregar cláusulas "Y":
fuente
Así es como lo haría:
Preciso y más eficiente que las otras respuestas. Implementa la técnica de eficiencia de "desenrollar el ciclo" de Friedl y requiere mucho menos retroceso.
fuente
Si desea hacer coincidir un carácter para negar una palabra similar a negar clase de caracteres:
Por ejemplo, una cadena:
No utilice:
Utilizar:
El aviso
"(?!bbb)."
no es mirar hacia atrás ni mirar hacia adelante, es actual, por ejemplo:fuente
(?!
). El prefijo de búsqueda hacia adelante positivo sería(?=
mientras que los prefijos de búsqueda hacia atrás correspondientes serían(?<!
y(?<=
respectivamente. Una búsqueda anticipada significa que lee los siguientes caracteres (por lo tanto, "adelante") sin consumirlos. Una mirada retrospectiva significa que verifica los caracteres que ya se han consumido.Una, en mi opinión, una variante más legible de la respuesta principal:
Básicamente, "coincide al comienzo de la línea si y solo si no tiene 'hede' en ella", por lo que el requisito se tradujo casi directamente en expresiones regulares.
Por supuesto, es posible tener múltiples requisitos de falla:
Detalles: el ancla ^ asegura que el motor de expresiones regulares no vuelva a intentar la coincidencia en cada ubicación de la cadena, lo que coincidiría con cada cadena.
El ancla ^ en el principio está destinada a representar el comienzo de la línea. La herramienta grep coincide con cada línea de una en una, en contextos en los que está trabajando con una cadena multilínea, puede usar el indicador "m":
o
fuente
El OP no especificó o Tagla publicación para indicar el contexto (lenguaje de programación, editor, herramienta) en el que se utilizará Regex.
Para mí, a veces necesito hacer esto mientras edito un archivo usando
Textpad
.Textpad
admite algunos Regex, pero no admite mirar hacia atrás o hacia atrás, por lo que se requieren algunos pasos.Si estoy buscando retener todas las líneas que NO contienen la cadena
hede
, lo haría así:Ahora tiene el texto original con todas las líneas que contienen la cadena
hede
eliminada.Si estoy buscando hacer algo más para solo líneas que NO contienen la cadena
hede
, lo haría así:fuente
Como nadie más ha dado una respuesta directa a la pregunta que se hizo , lo haré.
La respuesta es que con POSIX
grep
, es imposible satisfacer literalmente esta solicitud:La razón es que POSIX
grep
solo necesita trabajar con expresiones regulares básicas , que simplemente no son lo suficientemente potentes para realizar esa tarea (no son capaces de analizar lenguajes regulares, debido a la falta de alternancia y paréntesis).Sin embargo, GNU
grep
implementa extensiones que lo permiten. En particular,\|
es el operador de alternancia en la implementación de BRE de GNU,\(
y\)
son los paréntesis. Si su motor de expresión regular admite alternancia, expresiones de paréntesis negativas, paréntesis y la estrella de Kleene, y puede anclarse al principio y al final de la cadena, eso es todo lo que necesita para este enfoque. Sin embargo[^ ... ]
, tenga en cuenta que los conjuntos negativos son muy convenientes además de esos, porque de lo contrario, debe reemplazarlos con una expresión de la forma(a|b|c| ... )
que enumere todos los caracteres que no están en el conjunto, lo cual es extremadamente tedioso y demasiado largo, incluso más si todo el conjunto de caracteres es Unicode.Con GNU
grep
, la respuesta sería algo como:(encontrado con Grail y algunas optimizaciones adicionales hechas a mano).
También puede usar una herramienta que implemente Expresiones regulares extendidas , como
egrep
, para eliminar las barras invertidas:Aquí hay un script para probarlo (tenga en cuenta que genera un archivo
testinput.txt
en el directorio actual):En mi sistema imprime:
como se esperaba.
Para aquellos interesados en los detalles, la técnica empleada es convertir la expresión regular que coincide con la palabra en un autómata finito, luego invertir el autómata cambiando cada estado de aceptación a no aceptación y viceversa, y luego convirtiendo el FA resultante de nuevo a Una expresión regular.
Finalmente, como todos han notado, si su motor de expresión regular admite anticipación negativa, eso simplifica mucho la tarea. Por ejemplo, con GNU grep:
Actualización: Recientemente encontré la excelente biblioteca FormalTheory de Kendall Hopkins , escrita en PHP, que proporciona una funcionalidad similar a Grail. Utilizándolo, y un simplificador escrito por mí mismo, he podido escribir un generador en línea de expresiones regulares negativas con una frase de entrada (solo se admiten caracteres alfanuméricos y de espacio actualmente): http://www.formauri.es/personal/ pgimeno / misc / no-match-regex /
Para
hede
ello sale:que es equivalente a lo anterior.
fuente
Desde la introducción de ruby-2.4.1, podemos usar el nuevo operador ausente en las expresiones regulares de Ruby
del documento oficial
Por lo tanto, en su caso
^(?~hede)$
hace el trabajo por ustedfuente
A través del verbo PCRE
(*SKIP)(*F)
Esto omitiría por completo la línea que contiene la cadena exacta
hede
y coincide con todas las líneas restantes.MANIFESTACIÓN
Ejecución de las partes:
Consideremos la expresión regular anterior dividiéndola en dos partes.
Parte antes del
|
símbolo. La parte no debe coincidir .Parte después del
|
símbolo. Parte debe coincidir .PARTE 1
El motor Regex comenzará su ejecución desde la primera parte.
Explicación:
^
Afirma que estamos al principio.hede
Coincide con la cadenahede
$
Afirma que estamos al final de la línea.Entonces, la línea que contiene la cadena
hede
coincidiría. Una vez que el motor regex ve el siguiente verbo(*SKIP)(*F)
( Nota: podría escribir(*F)
como(*FAIL)
), salta y hace que la coincidencia falle.|
llamado alteración u operador lógico OR agregado al verbo PCRE que inturn coincide con todos los límites existentes entre todos y cada uno de los caracteres en todas las líneas, excepto que la línea contiene la cadena exactahede
. Vea la demostración aquí . Es decir, intenta hacer coincidir los caracteres de la cadena restante. Ahora se ejecutaría la expresión regular en la segunda parte.PARTE 2
Explicación:
^
Afirma que estamos al principio. es decir, coincide con todos los inicios de línea excepto el de lahede
línea. Vea la demostración aquí ..*
En el modo Multilínea,.
coincidiría con cualquier carácter, excepto los caracteres de nueva línea o retorno de carro. Y*
repetiría el carácter anterior cero o más veces. Entonces.*
coincidiría con toda la línea. Vea la demostración aquí .Hola, ¿por qué agregaste. * En lugar de. +?
Porque
.*
coincidiría con una línea en blanco pero.+
no coincidiría con un espacio en blanco. Queremos hacer coincidir todas las líneashede
, excepto que puede haber una posibilidad de líneas en blanco también en la entrada. así que debes usar en.*
lugar de.+
..+
repetiría el personaje anterior una o más veces. Ver.*
coincide con una línea en blanco aquí .$
El ancla de fin de línea no es necesaria aquí.fuente
Puede ser más fácil mantener dos expresiones regulares en su código, una para hacer la primera coincidencia, y luego, si coincide, ejecute la segunda expresión regular para verificar casos atípicos que desee bloquear, por ejemplo,
^.*(hede).*
entonces tenga la lógica apropiada en su código.OK, admito que esto no es realmente una respuesta a la pregunta publicada y también puede usar un poco más de procesamiento que una sola expresión regular. Pero para los desarrolladores que vinieron aquí buscando una solución de emergencia rápida para un caso atípico, esta solución no debe pasarse por alto.
fuente
Otra opción es agregar un look-ahead positivo y verificar si
hehe
está en algún lugar de la línea de entrada, entonces lo negaríamos, con una expresión similar a:con límites de palabras.
La expresión se explica en el panel superior derecho de regex101.com , si desea explorarla / simplificarla / modificarla, y en este enlace , puede ver cómo coincidiría con algunas entradas de muestra, si lo desea.
Circuito RegEx
jex.im visualiza expresiones regulares:
fuente
El lenguaje TXR admite la negación regex.
Un ejemplo más complicado: hacer coincidir todas las líneas que comienzan
a
y terminan conz
, pero no contienen la subcadenahede
:La negación Regex no es particularmente útil por sí sola, pero cuando también tiene intersección, las cosas se ponen interesantes, ya que tiene un conjunto completo de operaciones de conjunto booleano: puede expresar "el conjunto que coincide con esto, excepto las cosas que coinciden con eso".
fuente
La siguiente función lo ayudará a obtener el resultado deseado
fuente
^ ((?! hede).) * $ es una solución elegante, excepto porque consume caracteres que no podrá combinar con otros criterios. Por ejemplo, supongamos que desea verificar la no presencia de "hede" y la presencia de "jaja". Esta solución funcionaría porque no consumirá caracteres:
^ (?!. \ bhede \ b) (? =. \ bhaha \ b)
fuente
Cómo usar los verbos de control de retroceso de PCRE para que coincidan con una línea que no contiene una palabra
Aquí hay un método que no he visto usado antes:
Cómo funciona
Primero, trata de encontrar "hede" en algún lugar de la línea. Si tiene éxito, en este punto,
(*COMMIT)
le dice al motor que no solo retroceda en caso de falla, sino que no intente ninguna otra coincidencia en ese caso. Luego, intentamos hacer coincidir algo que no puede coincidir (en este caso^
).Si una línea no contiene "hede", la segunda alternativa, un subpatrón vacío, coincide con la cadena de asunto.
Este método no es más eficiente que una anticipación negativa, pero pensé que lo lanzaría aquí en caso de que alguien lo encuentre ingenioso y lo use para otras aplicaciones más interesantes.
fuente
¡Una solución más simple es usar el operador no !
Su declaración if deberá coincidir con "contiene" y no con "excluir".
Creo que los diseñadores de RegEx anticiparon el uso de no operadores.
fuente
Tal vez encuentre esto en Google mientras intenta escribir una expresión regular que pueda hacer coincidir segmentos de una línea (a diferencia de líneas completas) que no contienen una subcadena. Tócame un tiempo para averiguar, así que compartiré:
Dada una cadena:
<span class="good">bar</span><span class="bad">foo</span><span class="ugly">baz</span>
Quiero hacer coincidir las
<span>
etiquetas que no contienen la subcadena "mala"./<span(?:(?!bad).)*?>
coincidirá<span class=\"good\">
y<span class=\"ugly\">
.Observe que hay dos conjuntos (capas) de paréntesis:
Demo en Ruby:
fuente
Con ConyEdit , puede usar la línea de comando
cc.gl !/hede/
para obtener líneas que no contienen la coincidencia de expresiones regulares, o usar la línea de comandocc.dl /hede/
para eliminar las líneas que contienen la coincidencia de expresiones regulares. Tienen el mismo resultado.fuente
Quería añadir otro ejemplo de si usted está tratando de igualar toda una línea que contiene la cadena X , pero ¿también no contiene cadena de Y .
Por ejemplo, supongamos que queremos verificar si nuestra URL / cadena contiene " golosinas sabrosas ", siempre que no contenga también " chocolate " en ninguna parte.
Este patrón de expresiones regulares funcionaría (también funciona en JavaScript)
(banderas globales de líneas múltiples, por ejemplo)
Ejemplo interactivo: https://regexr.com/53gv4
Partidos
(Estas URL contienen "golosinas sabrosas" y tampoco contienen "chocolate")
No coincide
(Estas URL contienen "chocolate" en alguna parte, por lo que no coincidirán aunque contengan "golosinas sabrosas")
fuente