Expresión regular para coincidir con una línea que no contiene una palabra

Sé que es posible hacer coincidir una palabra y luego invertir las coincidencias con otras herramientas (p. Ej. grep -v). Sin embargo, ¿es posible hacer coincidir líneas que no contienen una palabra específica, p. Ej. 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

preguntado el 02 de enero de 09 a las 05:01

Probablemente un par de años tarde, pero ¿qué pasa con: ([^h]*(h([^e]|$)|he([^d]|$)|hed([^e]|$)))*? La idea es sencilla. 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 no esté terminada (donde N es la longitud de la cadena). Estos casos N-1 son "h seguido de non-e", "he seguido de non-d" y "hed seguido de non-e". Si logró pasar estos casos N-1, con éxito no Haga coincidir la cadena no deseada para que pueda comenzar a buscar [^h]* otra vez -

@stevendesu: intente esto con 'una-palabra-muy-muy-larga' o incluso mejor con la mitad de una oración. Diviértete escribiendo. Por cierto, es casi ilegible. No sé sobre el impacto en el rendimiento. -

@PeterSchuetze: Claro que no es bonito para palabras muy, muy largas, pero es una solución viable y correcta. Aunque no he realizado pruebas sobre el rendimiento, no me imagino que sea demasiado lento, ya que la mayoría de las últimas reglas se ignoran hasta que vea una h (o la primera letra de la palabra, oración, etc.). Y podría generar fácilmente la cadena de expresiones regulares para cadenas largas mediante la concatenación iterativa. Si funciona y se puede generar rápidamente, ¿es importante la legibilidad? Para eso están los comentarios. -

@stevendesu: estoy incluso más tarde, pero esa respuesta es casi completamente incorrecta. por un lado, requiere que el sujeto contenga "h" que no debería tener, dado que la tarea es "hacer coincidir líneas que [no] contienen una palabra específica". supongamos que pretendía hacer que el grupo interno fuera opcional y que el patrón está anclado: ^([^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". -

Esta pregunta se ha agregado a la Preguntas frecuentes sobre expresiones regulares de desbordamiento de pila, en "Advanced Regex-Fu". -

30 Respuestas

La noción de que la expresión regular no admite la coincidencia inversa no es del todo cierta. Puede imitar este comportamiento utilizando revisiones negativas:

^((?!hede).)*$

La expresión regular de arriba coincidirá con cualquier cadena o línea sin un salto de línea, no que contiene la (sub) cadena 'hede'. Como se mencionó, esto no es algo en lo que regex sea "bueno" (o debería hacer), pero aún así, is posible.

Y si también necesita hacer coincidir los caracteres de salto de línea, use el Modificador DOT-ALL (el final s en el siguiente patrón):

/^((?!hede).)*$/s

o utilícelo en línea:

/(?s)^((?!hede).)*$/

(donde el /.../ son 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 personaje [\s\S]:

/^((?!hede)[\s\S])*$/

Explicación

Una cadena es solo una lista de n caracteres. Antes y después de cada carácter, hay una cadena vacía. Entonces una lista de n los personajes tendrán n+1 cadenas vacías. Considere la cuerda "ABhedeCD":

    ┌──┬───┬──┬───┬──┬───┬──┬───┬──┬───┬──┬───┬──┬───┬──┬───┬──┐
S = │e1│ A │e2│ B │e3│ h │e4│ e │e5│ d │e6│ e │e7│ C │e8│ D │e9│
    └──┴───┴──┴───┴──┴───┴──┴───┴──┴───┴──┴───┴──┴───┴──┴───┴──┘

index    0      1      2      3      4      5      6      7

donde el eson las cadenas vacías. La expresión regular (?!hede). mira hacia adelante para ver si no hay subcadena "hede" para ser visto, y si ese es el caso (entonces se ve algo más), entonces el . (punto) coincidirá con cualquier carácter excepto con un salto de línea. Las miradas también se denominan aserciones de ancho cero porque ellos no consumir cualquier personaje. Solo afirman / validan algo.

Entonces, en mi ejemplo, cada cadena vacía se valida primero para ver si no hay "hede" más adelante, antes de que un personaje sea consumido por el . (punto). La expresión regular (?!hede). hará eso solo una vez, por lo que se envuelve en un grupo y se repite cero o más veces: ((?!hede).)*. Finalmente, el inicio y el final de la entrada están anclados para asegurarse de que se consuma toda la entrada: ^((?!hede).)*$

Como puede ver, la entrada "ABhedeCD" fallará porque en e3, la expresión regular (?!hede) falla (hay is "hede" ¡más adelante!).

contestado el 08 de mayo de 17 a las 21:05

No iría tan lejos como para decir que esto es algo en lo que la expresión regular es mala. La conveniencia de esta solución es bastante obvia y el impacto en el rendimiento en comparación con una búsqueda programática a menudo no será importante. - Arquímaredes

Estrictamente hablando, la vista previa negativa hace que las expresiones regulares no sean regulares. - Peter K

@PeterK, claro, pero esto es ASÍ, no MathOverflow o CS-Stackexchange. Las personas que hacen una pregunta aquí generalmente buscan una respuesta práctica. La mayoría de las bibliotecas o herramientas (como grep, que el OP menciona) con soporte para expresiones regulares, todos tienen características que los hacen no regulares en un sentido teórico. - bart kiers

@Bart Kiers, no te ofendas, responde, solo este abuso de terminología me irrita un poco. La parte realmente confusa aquí es que las expresiones regulares en sentido estricto pueden hacer mucho lo que OP quiere, pero el lenguaje común para escribirlas no lo permite, lo que conduce a soluciones alternativas (matemáticamente feas) como mirar hacia adelante. Por favor mira esta respuesta a continuación y mi comentario allí para (teóricamente alineado) la forma adecuada de hacerlo. No hace falta decir que funciona más rápido en grandes entradas. - Peter K

En caso de que alguna vez se haya preguntado cómo hacer esto en vim: ^\(\(hede\)\@!.\)*$ - calvos

Tenga en cuenta que la solución a no Empezar con "Hede":

^(?!hede).*$

es generalmente mucho más eficiente que la solución para no que no contengo "Hede":

^((?!hede).)*$

El primero busca "hede" sólo en la primera posición de la cadena de entrada, en lugar de en cada posición.

Respondido 27 ago 13, 17:08

Gracias, lo usé para validar que la cadena no contiene una secuencia de dígitos ^ ((?! \ D {5,}).) * - samih un

¡Hola! No puedo componer no final con "hede" regex. ¿Puedes ayudarme con eso? - aleks ya

@AleksYa: simplemente use la versión "contener" e incluya el ancla final en la cadena de búsqueda: cambie la cadena a "no coincide" de "hede" a "hede $" - Nyerguds

@AleksYa: la versión no final se puede hacer usando una búsqueda hacia atrás negativa como: (.*)(?<!hede)$. La versión de @Nyerguds también funcionaría, pero pierde por completo el punto sobre el rendimiento que menciona la respuesta. - este es mi diseño

¿Por qué tantas respuestas dicen ^((?!hede).)*$ ? ¿No es más eficiente usar ^(?!.*hede).*$ ? Hace lo mismo pero en menos pasos: JackPRead

If solo lo está usando para grep, puede usar grep -v hede para obtener todas las líneas que no contienen hede.

ETA Oh, releyendo la pregunta, grep -v es probablemente lo que quiso decir con "opciones de herramientas".

Respondido el 02 de enero de 09 a las 07:01

Sugerencia: para filtrar progresivamente lo que no desea: grep -v "hede" | grep -v "hihi" | ... etc. - Olivier Lalonde

O usando solo un proceso grep -v -e hede -e hihi -e ... - Olaf Dietsche

O solo grep -v "hede\|hihi" :) - Putnik

Si tiene muchos patrones que desea filtrar, colóquelos en un archivo y use grep -vf pattern_file file - códigoforester

O simplemente egrep or grep -Ev "hede|hihi|etc" para evitar el incómodo escape. - Amit Naidu

Respuesta

^((?!hede).)*$

Explicación:

^el comienzo de la cadena, ( agrupar y capturar hasta \ 1 (0 o más veces (coincidiendo con la mayor cantidad posible)),
(?! mira hacia adelante para ver si no la hay,

hede tu cuerda,

) fin de la anticipación, . cualquier carácter excepto \ n,
)* final 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 al final de la cadena

Respondido el 06 de diciembre de 17 a las 11:12

impresionante que funcionó para mí en texto sublime 2 usando varias palabras '^((?!DSAU_PW8882WEB2|DSAU_PW8884WEB2|DSAU_PW8884WEB).)*$'- Bashyal de Damodar

@DamodarBashyal Sé que llego bastante tarde aquí, pero podrías eliminar totalmente el segundo término allí y obtendrías exactamente los mismos resultados: forresthopkinsa

Las respuestas dadas están perfectamente bien, solo un punto académico:

Expresiones regulares en el sentido de las ciencias de la computación teóricas NO SON CAPAZ Hazlo asi. Para ellos tenía que verse así:

^([^h].*$)|(h([^e].*$|$))|(he([^h].*$|$))|(heh([^e].*$|$))|(hehe.+$) 

Esto solo hace una coincidencia COMPLETA. Hacerlo para sub-partidos sería incluso más incómodo.

Respondido el 02 de Septiembre de 11 a las 16:09

Es importante tener en cuenta que esto solo usa expresiones regulares POSIX.2 básicas y, por lo tanto, aunque conciso es más portátil para cuando PCRE no está disponible. - steve-o

Estoy de acuerdo. Muchas, si no la mayoría, de las expresiones regulares no son lenguajes regulares y no podrían ser reconocidas por un autómata finito. - Thomas McLeod

@ThomasMcLeod, Hades32: ¿Está dentro del ámbito de cualquier posible lenguaje regular poder decir 'no'Y'y'así como el'or'de una expresión como'(hede|Hihi)'? (Esta quizás sea una pregunta para CS). - james haigh

@JohnAllen: ¡¡¡YO!!! … Bueno, no la expresión regular real sino la referencia académica, que también se relaciona estrechamente con la complejidad computacional; Básicamente, los PCRE no pueden garantizar la misma eficiencia que las expresiones regulares POSIX. - james haigh

Lo siento, esta respuesta simplemente no funciona, coincidirá con hhehe e incluso coincidirá con jeje parcialmente (la segunda mitad) - Falco

Si desea que la prueba de expresiones regulares , solamente fallar si el cuerda entera coincidencias, lo siguiente funcionará:

^(?!hede$).*

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á buscando exacto igualdad, una mejor solución general en este caso es verificar la igualdad de cadenas, es decir

myStr !== 'foo'

Incluso podrías poner la negación afuera la prueba si necesita alguna característica de expresiones regulares (aquí, no se distingue entre mayúsculas y minúsculas y coincidencia de rango):

!/^[a-f]oo$/i.test(myStr)

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 (tal vez mediante una API).

respondido 07 nov., 18:21

¿Qué pasa con los espacios en blanco al final? Por ejemplo, si quiero que la prueba falle con una cadena " hede "? - éagor

@eagor el \s directiva coincide con un solo carácter de espacio en blanco - Roy Tinker

gracias, pero no pude actualizar la expresión regular para que esto funcione. - éagor

@eagor: ^(?!\s*hede\s*$).* - Roy Tinker

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}, sufijo).

Primero define el tipo de sus expresiones: las etiquetas son letras (lal_char) para elegir a a z por ejemplo (definir el alfabeto cuando se trabaja con complementación es, por supuesto, muy importante), y el "valor" calculado para cada palabra es solo un booleano: true la palabra es aceptada, false, rechazado.

En Python:

In [5]: import vcsn
        c = vcsn.context('lal_char(a-z), b')
        c
Out[5]: {a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z} → 𝔹

luego ingresa su expresión:

In [6]: e = c.expression('(hede){c}'); e
Out[6]: (hede)^c

convierte esta expresión en un autómata:

In [7]: a = e.automaton(); a

El autómata correspondiente

finalmente, convierta este autómata en una expresión simple.

In [8]: print(a.expression())
        \e+h(\e+e(\e+d))+([^h]+h([^e]+e([^d]+d([^e]+e[^]))))[^]*

dónde + generalmente se denota |, \e denota la palabra vacía, y [^] generalmente está escrito . (cualquier personaje). Entonces, con un poco de reescritura ()|h(ed?)?|([^h]|h([^e]|e([^d]|d([^e]|e.)))).*.

Puedes ver este ejemplo aquíy pruebe Vcsn en línea ahí.

Respondido el 08 de enero de 18 a las 23:01

Cierto, pero feo, y solo es factible para pequeños conjuntos de caracteres. No quieres hacer esto con cadenas Unicode :-) - reinierpost

Hay más herramientas que lo permiten, siendo una de las más impresionantes Rabia. Allí se escribiría como (any * - ('hehe' any *)) para una coincidencia alineada al inicio o (any * - ('hehe' any *)) para unaligned. - Peter K

@reinierpost: ¿por qué es feo y cuál es el problema con unicode? No puedo estar de acuerdo con ambos. (No tengo experiencia con vcsn, pero sí con DFA). - Peter K

@PedroGimeno Cuando anclaste, ¿te aseguraste de poner esta expresión regular en parens primero? De lo contrario, las precedencias entre anclas y | no jugará bien. '^(()|h(ed?)?|([^h]|h([^e]|e([^d]|d([^e]|e.)))).*)$'. - akim

Creo que vale la pena señalar que este método es para hacer coincidir líneas que no son la palabra 'hede', en lugar de líneas que no contienen la palabra 'hede' que es lo que pidió el OP. Vea mi respuesta para este último. - Pedro Gimeno

aquí está una buena explicacion 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 es algo más que una pregunta hipotética, entonces una expresión regular no es la opción correcta aquí.

Respondido el 02 de enero de 09 a las 08:01

Algunas herramientas, y específicamente mysqldumpslow, solo ofrecen esta forma de filtrar datos, por lo que, en tal caso, encontrar una expresión regular para hacer esto es la mejor solución además de reescribir la herramienta (MySQL AB / Sun no ha incluido varios parches para esto / Oracle. - MGF

Exactamente análogo a mi situación. El motor de plantillas Velocity usa expresiones regulares para decidir cuándo aplicar una transformación (escape html) y quiero que siempre funcione EXCEPTO en una situación. - henno vermeulen

¿Qué alternativa hay? Nunca he encontrado nada que pueda hacer una coincidencia precisa de cadenas además de expresiones regulares. Si OP está utilizando un lenguaje de programación, es posible que haya otras herramientas disponibles, pero si no utiliza código de escritura, probablemente no haya otra opción. - reyfrito_5005

Uno de los muchos escenarios no hipotéticos en los que una expresión regular es la mejor opción disponible: estoy en un IDE (Android Studio) que muestra la salida del registro y las únicas herramientas de filtrado proporcionadas son: cadenas simples y expresiones regulares. Intentar hacer esto con cadenas simples sería un completo fracaso. - LarsH

Con una búsqueda anticipada 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 probará de 1 a 4 caracteres por delante mientras coincide con cualquier carácter. Podemos evitar esto y dejar que la parte de anticipación revise todo el texto, asegurarnos de que no haya 'hede', y luego la parte normal (. *) Puede comerse todo el texto de una vez.

Aquí está la expresión regular mejorada:

/^(?!.*?hede).*$/

Tenga en cuenta que el cuantificador perezoso (*?) En la parte de anticipación negativa es opcional, puede usar (*) cuantificador codicioso en su lugar, dependiendo de sus datos: si 'hede' está presente y en la mitad inicial del texto, el cuantificador perezoso puede se más rápido; de lo contrario, el cuantificador codicioso será más rápido. Sin embargo, si 'hede' no está presente, ambos serían igualmente lentos.

Aquí está la código de demostración.

Para obtener más información sobre la anticipación, consulte el excelente artículo: Dominando Lookahead y Lookbehind.

Además, echa un vistazo 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:

var _ = regexGen;

var regex = _(
    _.startOfLine(),             
    _.anything().notContains(       // match anything that not contains:
        _.anything().lazy(), 'hede' //   zero or more chars that followed by 'hede',
                                    //   i.e., anything contains 'hede'
    ), 
    _.endOfLine()
);

Respondido 14 Jul 14, 19:07

así que para simplemente verificar si la cadena dada no contiene str1 y str2: ^(?!.*(str1|str2)).*$ - S. Serpooshan

Sí, o puede usar un cuantificador diferido: ^(?!.*?(?:str1|str2)).*$, dependiendo de sus datos. Agregó el ?: ya que no necesitamos capturarlo. - amobiz

Esta es, con mucho, la mejor respuesta en un factor de 10xms. Si agregó su código jsfiddle y los resultados en la respuesta, la gente podría notarlo. Me pregunto por qué la versión perezosa es más rápida que la versión codiciosa cuando no hay hede. ¿No deberían tomar la misma cantidad de tiempo? - user5389726598465

Sí, toman la misma cantidad de tiempo ya que ambos prueban todo el texto. - amobiz

Los puntos de referencia

Decidí evaluar algunas de las opciones presentadas y comparar su rendimiento, así como utilizar algunas funciones nuevas. Evaluación comparativa en .NET Regex Engine: http://regexhero.net/tester/

Texto de referencia:

Las primeras 7 líneas no deben coincidir, ya que contienen la expresión buscada, mientras que las 7 líneas inferiores deben coincidir.

Regex Hero is a real-time online Silverlight Regular Expression Tester.
XRegex Hero is a real-time online Silverlight Regular Expression Tester.
Regex HeroRegex HeroRegex HeroRegex HeroRegex Hero is a real-time online Silverlight Regular Expression Tester.
Regex Her Regex Her Regex Her Regex Her Regex Her Regex Her Regex Hero is a real-time online Silverlight Regular Expression Tester.
Regex Her is a real-time online Silverlight Regular Expression Tester.Regex Hero
egex Hero egex Hero egex Hero egex Hero egex Hero egex Hero Regex Hero is a real-time online Silverlight Regular Expression Tester.
RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRegex Hero is a real-time online Silverlight Regular Expression Tester.

Regex Her
egex Hero
egex Hero is a real-time online Silverlight Regular Expression Tester.
Regex Her is a real-time online Silverlight Regular Expression Tester.
Regex Her Regex Her Regex Her Regex Her Regex Her Regex Her is a real-time online Silverlight Regular Expression Tester.
Nobody is a real-time online Silverlight Regular Expression Tester.
Regex Her o egex Hero Regex  Hero Reg ex Hero is a real-time online Silverlight Regular Expression Tester.

Resultados:

Los resultados son iteraciones por segundo como la mediana de 3 ejecuciones: Número más grande = mejor

01: ^((?!Regex Hero).)*$                    3.914   // Accepted Answer
02: ^(?:(?!Regex Hero).)*$                  5.034   // With Non-Capturing group
03: ^(?>[^R]+|R(?!egex Hero))*$             6.137   // Lookahead only on the right first letter
04: ^(?>(?:.*?Regex Hero)?)^.*$             7.426   // Match the word and check if you're still at linestart
05: ^(?(?=.*?Regex Hero)(?#fail)|.*)$       7.371   // Logic Branch: Find Regex Hero? match nothing, else anything

P1: ^(?(?=.*?Regex Hero)(*FAIL)|(*ACCEPT))  ?????   // Logic Branch in Perl - Quick FAIL
P2: .*?Regex Hero(*COMMIT)(*FAIL)|(*ACCEPT) ?????   // Direct COMMIT & FAIL in Perl

Dado que .NET no admite verbos de acción (* FAIL, etc.), no pude probar las soluciones P1 y P2.

Resumen:

Traté de probar la mayoría de las soluciones propuestas, algunas optimizaciones son posibles para ciertas palabras. Por ejemplo, si las dos primeras letras de la cadena de búsqueda no son iguales, la respuesta 03 se puede expandir a ^(?>[^R]+|R+(?!egex Hero))*$ resultando en una pequeña ganancia de rendimiento.

Pero la solución más rápida más legible y más rápida en términos de rendimiento parece ser 05 usando una declaración condicional o 04 con el cuantificador posesivo. Creo que las soluciones de Perl deberían ser incluso más rápidas y fáciles de leer.

Respondido el 20 de junio de 20 a las 10:06

Deberías cronometrar ^(?!.*hede) también. /// 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. - Ikegami

No regex, pero me parece lógico y útil usar greps en serie con pipe para eliminar el ruido.

p.ej. buscar un archivo de configuración de apache sin todos los comentarios-

grep -v '\#' /opt/lampp/etc/httpd.conf      # this gives all the non-comment lines

y

grep -v '\#' /opt/lampp/etc/httpd.conf |  grep -i dir

La lógica de los grep seriales es (no un comentario) y (coincide con dir)

respondido 17 mar '11, 20:03

Creo que está pidiendo la versión regex del grep -v - Ángel.King.47

Esto es peligroso. También pierde líneas como good_stuff #comment_stuff - Xavi Montero

con esto, evita probar una anticipación en cada posición:

/^(?:[^h]+|h++(?!ede))*+$/

equivalente a (para .net):

^(?>(?:[^h]+|h+(?!ede))*)$

Respuesta anterior:

/^(?>[^h]+|h+(?!ede))*$/

Respondido el 04 de junio de 18 a las 11:06

Buen punto; Me sorprende que nadie haya mencionado este enfoque antes. Sin embargo, esa expresión regular en particular es propensa a retroceso catastrófico cuando se aplica a texto que no coincide. Así es como lo haría: /^[^h]*(?:h+(?!ede)[^h]*)*$/ - Alan Moore

... o simplemente puede hacer que todos los cuantificadores sean posesivos. ;) - Alan Moore

@Alan Moore - Yo también estoy sorprendido. Vi su comentario (y la mejor expresión regular en la pila) aquí solo después de publicar este mismo patrón en una respuesta a continuación. - ridgerunner

@ridgerunner, aunque no tiene por qué ser el mejor. He visto puntos de referencia en los que la respuesta principal funciona mejor. (Me sorprendió eso aunque) - Qimpuesto

Citado (?:(?!hede).)* es genial porque se puede anclar.

^(?:(?!hede).)*$               # A line without hede

foo(?:(?!hede).)*bar           # foo followed by bar, without hede between them

Pero lo siguiente sería suficiente en este caso:

^(?!.*hede)                    # A line without hede

Esta simplificación está lista para agregar cláusulas "Y":

^(?!.*hede)(?=.*foo)(?=.*bar)   # A line with foo and bar, but without hede
^(?!.*hede)(?=.*foo).*bar       # Same

Respondido 23 ago 16, 01:08

Así es como lo haría:

^[^h]*(h(?!ede)[^h]*)*$

Preciso y más eficiente que las otras respuestas. Implementa Friedl's "desenrollar el bucle" técnica de eficiencia y requiere mucho menos retroceso.

Respondido el 20 de diciembre de 13 a las 03:12

Una, en mi opinión, una variante más legible de la respuesta principal:

^(?!.*hede)

Básicamente, "haga coincidir al principio de la línea si y solo si no tiene 'hede' en ella", por lo que el requisito se traduce casi directamente en expresiones regulares.

Por supuesto, es posible tener varios requisitos de falla:

^(?!.*(hede|hodo|hada))

Detalles: El ancla ^ asegura que el motor de expresiones regulares no vuelva a intentar la coincidencia en cada ubicación de la cadena, que coincidiría con todas las cadenas.

El ancla ^ al principio está destinado a representar el principio de la línea. La herramienta grep hace coincidir cada línea de una en una, en contextos en los que está trabajando con una cadena de varias líneas, puede usar la marca "m":

/^(?!.*hede)/m # JavaScript syntax

or

(?m)^(?!.*hede) # Inline flag

Respondido el 08 de diciembre de 18 a las 20:12

Excelente ejemplo con negación múltiple. - Pedro Parada

Una diferencia con la respuesta principal es que esto no coincide con nada y coincide con toda la línea si no tiene "hede" - Bernardo Dal Corno

Si desea hacer coincidir un carácter para negar una palabra similar a negar la clase de carácter:

Por ejemplo, una cadena:

<?
$str="aaa        bbb4      aaa     bbb7";
?>

No utilice:

<?
preg_match('/aaa[^bbb]+?bbb7/s', $str, $matches);
?>

Uso:

<?
preg_match('/aaa(?:(?!bbb).)+?bbb7/s', $str, $matches);
?>

Aviso "(?!bbb)." no es ni retrospectiva ni anticipada, es lookcurrent, por ejemplo:

"(?=abc)abcde", "(?!abc)abcde"

Respondido 03 Abr '14, 17:04

No hay "lookcurrent" en las expresiones regulares de perl. Esta es realmente una búsqueda anticipada negativa (prefijo (?!). El prefijo de búsqueda anticipada positiva sería (?= mientras que los prefijos de búsqueda 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 atrás significa que verifica los personajes que ya se han consumido. - didier l

No estoy seguro de cómo (?!abc)abcde tiene algún sentido en absoluto. - Scratte

Dado que nadie más ha dado una respuesta directa a la pregunta que fue preguntado, Lo haré.

La respuesta es que con POSIX grep, es imposible satisfacer literalmente esta solicitud:

grep "<Regex for 'doesn't contain hede'>" input

La razón es que POSIX grep solo se requiere para trabajar con Expresiones regulares básicas, que simplemente no son lo suficientemente potentes para realizar esa tarea (no son capaces de analizar todos los lenguajes regulares debido a la falta de alternancia).

Sin embargo, GNU grep implementa extensiones que lo permiten. En particular, \| es el operador de alternancia en la implementación de BRE de GNU. Si su motor de expresión regular admite alternancia, paréntesis y la estrella de Kleene, y puede anclar 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 enumera 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.

Gracias a la teoría del lenguaje formal, podemos ver cómo se ve esa expresión. Con GNU grep, la respuesta sería algo como:

grep "^\([^h]\|h\(h\|eh\|edh\)*\([^eh]\|e[^dh]\|ed[^eh]\)\)*\(\|h\(h\|eh\|edh\)*\(\|e\|ed\)\)$" input

(encontrado con Grial y algunas optimizaciones adicionales hechas a mano).

También puede utilizar una herramienta que implemente Expresiones regulares extendidas, me gusta egrep, para deshacerse de las barras invertidas:

egrep "^([^h]|h(h|eh|edh)*([^eh]|e[^dh]|ed[^eh]))*(|h(h|eh|edh)*(|e|ed))$" input

Aquí hay un script para probarlo (tenga en cuenta que genera un archivo testinput.txt en el directorio actual). Varias de las expresiones presentadas fallan en esta prueba.

#!/bin/bash
REGEX="^\([^h]\|h\(h\|eh\|edh\)*\([^eh]\|e[^dh]\|ed[^eh]\)\)*\(\|h\(h\|eh\|edh\)*\(\|e\|ed\)\)$"

# First four lines as in OP's testcase.
cat > testinput.txt <<EOF
hoho
hihi
haha
hede

h
he
ah
head
ahead
ahed
aheda
ahede
hhede
hehede
hedhede
hehehehehehedehehe
hedecidedthat
EOF
diff -s -u <(grep -v hede testinput.txt) <(grep "$REGEX" testinput.txt)

En mi sistema imprime:

Files /dev/fd/63 and /dev/fd/62 are identical

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 convertir el FA resultante de nuevo en una expresión regular.

Como todos han notado, si su motor de expresión regular admite una búsqueda anticipada negativa, la expresión regular es mucho más simple. Por ejemplo, con GNU grep:

grep -P '^((?!hede).)*$' input

Sin embargo, este enfoque tiene la desventaja de que requiere un motor de expresión regular de retroceso. Esto lo hace inadecuado en instalaciones que utilizan motores de expresión regular seguros como RE2, que es una de las razones para preferir el enfoque generado en algunas circunstancias.

Usando el excelente de Kendall Hopkins Teoría formal biblioteca, escrita en PHP, que proporciona una funcionalidad similar a Grail, y un simplificador escrito por mí mismo, he podido escribir un generador en línea de expresiones regulares negativas dada una frase de entrada (solo se admiten caracteres alfanuméricos y de espacio actualmente): http://www.formauri.es/personal/pgimeno/misc/non-match-regex/

Para hede produce:

^([^h]|h(h|e(h|dh))*([^eh]|e([^dh]|d[^eh])))*(h(h|e(h|dh))*(ed?)?)?$

que es equivalente a lo anterior.

Respondido 01 Oct 20, 16:10

El PO no especificó ni Etiqueta la publicación para indicar el contexto (lenguaje de programación, editor, herramienta) en el que se utilizará la expresión regular.

Para mí, a veces necesito hacer esto mientras edito un archivo usando Textpad.

Textpad admite algunas expresiones regulares, pero no admite la búsqueda anticipada ni la retrospectiva, por lo que se necesitan algunos pasos.

Si busco retener todas las líneas que No haga contener la cadena hede, Lo haría así:

1. Busque / reemplace todo el archivo para agregar una "Etiqueta" única al comienzo de cada línea que contenga cualquier texto.

    Search string:^(.)  
    Replace string:<@#-unique-#@>\1  
    Replace-all  

2. Elimina todas las líneas que contienen la cadena. hede (la cadena de reemplazo está vacía):

    Search string:<@#-unique-#@>.*hede.*\n  
    Replace string:<nothing>  
    Replace-all  

3. En este punto, todas las líneas restantes No haga contener la cadena hede. Elimine la "Etiqueta" única de todas las líneas (la cadena de reemplazo está vacía):

    Search string:<@#-unique-#@>
    Replace string:<nothing>  
    Replace-all  

Ahora tienes el texto original con todas las líneas que contienen la cadena. hede eliminado.


Si estoy buscando Hacer algo más a solo líneas que No haga contener la cadena hede, Lo haría así:

1. Busque / reemplace todo el archivo para agregar una "Etiqueta" única al comienzo de cada línea que contenga cualquier texto.

    Search string:^(.)  
    Replace string:<@#-unique-#@>\1  
    Replace-all  

2. Para todas las líneas que contienen la cadena hede, elimine la "Etiqueta" única:

    Search string:<@#-unique-#@>(.*hede)
    Replace string:\1  
    Replace-all  

3. En este punto, todas las líneas que comienzan con la "Etiqueta" única, No haga contener la cadena hede. Ahora puedo hacer mi Otro asunto a solo esas líneas.

4. Cuando termine, elimino la "Etiqueta" única de todas las líneas (la cadena de reemplazo está vacía):

    Search string:<@#-unique-#@>
    Replace string:<nothing>  
    Replace-all  

Respondido 26 Abr '13, 23:04

Desde la introducción de ruby-2.4.1, podemos usar el nuevo Operador ausente en las expresiones regulares de Ruby

del oficial doctor

(?~abc) matches: "", "ab", "aab", "cccc", etc.
It doesn't match: "abc", "aabc", "ccccabc", etc.

Así, en tu caso ^(?~hede)$ hace el trabajo por ti

2.4.1 :016 > ["hoho", "hihi", "haha", "hede"].select{|s| /^(?~hede)$/.match(s)}
 => ["hoho", "hihi", "haha"]

respondido 23 mar '17, 13:03

Otra opción es agregar una anticipación positiva y verificar si hede está en cualquier lugar de la línea de entrada, entonces negaríamos eso, con una expresión similar a:

^(?!(?=.*\bhede\b)).*$

con límites de palabras.


La expresión se explica en el panel superior derecho de Regex101.com, si desea explorarlo / simplificarlo / modificarlo, y en este link, puede ver cómo coincidiría con algunas entradas de muestra, si lo desea.


Circuito RegEx

jex.im visualiza expresiones regulares:

enter image description here

Respondido 15 Jul 20, 11:07

No entiendo cómo es útil la anticipación positiva "interna". - Scratte

A través del verbo PCRE (*SKIP)(*F)

^hede$(*SKIP)(*F)|^.*$

Esto salta completamente la línea que contiene la cadena exacta hede y coincide con todas las líneas restantes.

DEMO

Ejecución de las piezas:

Consideremos la expresión regular anterior dividiéndola en dos partes.

  1. Parte antes del | símbolo. Parte no debería coincidir.

    ^hede$(*SKIP)(*F)
    
  2. Parte después de la | símbolo. Parte debe coincidir.

    ^.*$
    

PARTE 1

El motor Regex comenzará su ejecución desde la primera parte.

^hede$(*SKIP)(*F)

Explicación:

  • ^ Afirma que estamos al principio.
  • hede Coincide con la cuerda hede
  • $ 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 de expresiones regulares ve lo siguiente (*SKIP)(*F) (Nota: podrías escribir (*F) as (*FAIL)) verbo, salta y hace que la coincidencia falle. | llamado alteración u operador lógico OR agregado al lado del verbo PCRE que a su vez coincide con todos los límites que existen entre todos y cada uno de los caracteres en todas las líneas, excepto que la línea contiene la cadena exacta hede. Ver la demo aquí. Es decir, intenta hacer coincidir los caracteres de la cadena restante. Ahora se ejecutará la expresión regular de la segunda parte.

PARTE 2

^.*$

Explicación:

  • ^ Afirma que estamos al principio. es decir, coincide con todos los comienzos de línea excepto el que está en el hede línea. Ver la demo 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. Ver la demo aquí.

    Oye, ¿por qué agregaste. * En lugar de. +?

    Porque .* coincidiría con una línea en blanco pero .+ no coincidirá con un espacio en blanco. Queremos hacer coincidir todas las líneas excepto hede , puede haber una posibilidad de líneas en blanco también en la entrada. entonces debes usar .* en lugar de .+ . .+ repetiría el carácter 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í.

Respondido 09 Oct 14, 08:10

Puede ser más fácil de 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 los casos atípicos que desea bloquear, por ejemplo ^.*(hede).* luego tenga la lógica apropiada en su código.

De acuerdo, 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í en busca de una solución de emergencia rápida para un caso atípico, esta solución no debe pasarse por alto.

Respondido el 13 de Septiembre de 16 a las 14:09

La Idioma TXR admite la negación de expresiones regulares.

$ txr -c '@(repeat)
@{nothede /~hede/}
@(do (put-line nothede))
@(end)'  Input

Un ejemplo más complicado: haga coincidir todas las líneas que comienzan con a y terminar con z, pero no contienen la subcadena hede:

$ txr -c '@(repeat)
@{nothede /a.*z&~.*hede.*/}
@(do (put-line nothede))
@(end)' -
az         <- echoed
az
abcz       <- echoed
abcz
abhederz   <- not echoed; contains hede
ahedez     <- not echoed; contains hede
ace        <- not echoed; does not end in z
ahedz      <- echoed
ahedz

La negación de expresiones regulares 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 conjuntos booleanos: puede expresar "el conjunto que coincide con esto, excepto las cosas que coinciden con eso".

Respondido el 25 de junio de 14 a las 02:06

Tenga en cuenta que también es la solución para expresiones regulares basadas en ElasticSearch Lucene. - Wiktor Stribiżew

La siguiente función le ayudará a obtener el resultado deseado

<?PHP
      function removePrepositions($text){

            $propositions=array('/\bfor\b/i','/\bthe\b/i'); 

            if( count($propositions) > 0 ) {
                foreach($propositions as $exceptionPhrase) {
                    $text = preg_replace($exceptionPhrase, '', trim($text));

                }
            $retval = trim($text);

            }
        return $retval;
    }


?>

respondido 11 mar '17, 01:03

Siempre que esté tratando con líneas, simplemente marcar las coincidencias negativas y apuntar al resto.

De hecho, utilizo este truco con sed porque ^((?!hede).)*$ parece que no es compatible con él.

Para la salida deseada

  1. Marque la coincidencia negativa: (por ejemplo, líneas con hede), utilizando un carácter que no está incluido en todo el texto. Un emoji probablemente podría ser una buena opción para este propósito.

    s/(.*hede)/🔒\1/g
    
  2. Apunte al resto (las cadenas sin marcar: por ejemplo, líneas sin hede). Suponga que quiere mantener solo el objetivo y borra el resto (como quieras):

    s/^🔒.*//g
    

Para una mejor comprensión

Supongamos que quieres eliminar el objetivo:

  1. Marque la coincidencia negativa: (por ejemplo, líneas con hede), utilizando un carácter que no está incluido en todo el texto. Un emoji probablemente podría ser una buena opción para este propósito.

    s/(.*hede)/🔒\1/g
    
  2. Apunte al resto (las cadenas sin marcar: por ejemplo, líneas sin hede). Suponga que quiere eliminar el objetivo:

    s/^[^🔒].*//g
    
  3. Eliminar la marca:

    s/🔒//g
    

Respondido el 15 de junio de 20 a las 12:06

Quería agregar otro ejemplo si está tratando de hacer coincidir una línea completa que contiene una cadena X, pero no también contiene cadena Y.

Por ejemplo, digamos que queremos comprobar si nuestra URL / cadena contiene "golosinas-sabrosas", siempre que no contenga también"chocolate" en cualquier lugar.

Este patrón de expresiones regulares funcionaría (también funciona en JavaScript)

^(?=.*?tasty-treats)((?!chocolate).)*$

(banderas globales, multilínea en el ejemplo)

Ejemplo interactivo: https://regexr.com/53gv4

Cerillas

(Estas URL contienen "golosinas deliciosas" y tampoco contienen "chocolate")

  • example.com/tasty-treats/strawberry-ice-cream
  • ejemplo.com/desserts/tasty-treats/banana-pudding
  • example.com/tasty-treats-overview

No coincide

(Estas URL contienen "chocolate" en alguna parte, por lo que no coincidirán aunque contengan "golosinas deliciosas")

  • ejemplo.com/tasty-treats/pastel-de-chocolate
  • ejemplo.com/cocina-casera/pollo-asado-al-horno
  • ejemplo.com/tasty-treats/banana-chocolate-fudge
  • ejemplo.com/desserts/chocolate/tasty-treats
  • ejemplo.com/chocolate/tasty-treats/desserts

Respondido el 20 de junio de 20 a las 10:06

^((?!hede).)*$ es una solución elegante, excepto que por consumir caracteres no podrás combinarla con otros criterios. Por ejemplo, supongamos que desea comprobar la ausencia de "hede" y la presencia de "jaja". Esta solución funcionaría porque no consumirá caracteres:

^(?!.*\bhede\b)(?=.*\bhaha\b) 

Respondido el 10 de junio de 20 a las 23:06

Cómo usar los verbos de control de retroceso de PCRE para hacer coincidir una línea que no contiene una palabra

Aquí hay un método que no he visto antes utilizado:

/.*hede(*COMMIT)^|/

Cómo funciona

Primero, intenta encontrar "hede" en algún lugar de la línea. Si tiene éxito, en este punto, (*COMMIT) le dice al motor que no solo no retroceda en caso de falla, sino que tampoco intente más coincidencias en ese caso. Luego, intentamos hacer coincidir algo que posiblemente no pueda coincidir (en este caso, ^).

Si una línea no contiene "hede", la segunda alternativa, un subpatrón vacío, coincide correctamente con la cadena del asunto.

Este método no es más eficiente que una búsqueda anticipada negativa, pero pensé que lo usaría aquí en caso de que alguien lo encuentre ingenioso y encuentre un uso para otras aplicaciones más interesantes.

Respondido 11 Oct 17, 11:10

Una solución más sencilla es utilizar el operador not !

Tus if La declaración deberá coincidir con "contiene" y no con "excluye".

var contains = /abc/;
var excludes =/hede/;

if(string.match(contains) && !(string.match(excludes))){  //proceed...

Creo que los diseñadores de RegEx anticiparon el uso de no operadores.

Respondido el 13 de Septiembre de 16 a las 15:09

Tal vez encuentre esto en Google mientras intenta escribir una expresión regular que sea capaz de hacer coincidir segmentos de una línea (a diferencia de líneas completas) que lo hacen no contener una subcadena. Me tomó un tiempo averiguarlo, así que compartiré:

Dada una cadena: <span class="good">bar</span><span class="bad">foo</span><span class="ugly">baz</span>

Quiero igualar <span> etiquetas que no contienen la subcadena "bad".

/<span(?:(?!bad).)*?> coincidirá <span class=\"good\"> y <span class=\"ugly\">.

Observe que hay dos conjuntos (capas) de paréntesis:

  • El más interno es para la búsqueda anticipada negativa (no es un grupo de captura)
  • El más externo fue interpretado por Ruby como un grupo de captura pero no queremos que sea un grupo de captura, así que agregué?: Al principio y ya no se interpreta como un grupo de captura.

Demo en Ruby:

s = '<span class="good">bar</span><span class="bad">foo</span><span class="ugly">baz</span>'
s.scan(/<span(?:(?!bad).)*?>/)
# => ["<span class=\"good\">", "<span class=\"ugly\">"]

Respondido 25 Abr '18, 19:04

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.