Pregunta Expresión regular para que coincida 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. grep -v) Sin embargo, me gustaría saber si es posible hacer coincidir líneas que no lo hagas contener 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

3559


origen


Respuestas:


La noción de que Regex no es compatible con la coincidencia inversa no es del todo cierto. Puede imitar este comportamiento utilizando miradas negativas:

^((?!hede).)*$

La expresión regular anterior 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 que regex sea "bueno" en (o debería), pero aun así, es posible.

Y si también necesita hacer coincidir los caracteres line break chars, use el Modificador DOT-ALL (el final s en el siguiente patrón):

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

o usarlo 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, puedes 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 personaje, hay una cadena vacía. Entonces una lista de n los personajes tendrán n+1 cadenas vacías. Considera la cadena "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 cuerdas 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 (por lo que 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 lo hacen consumir cualquier personaje Solo afirman / validan algo.

Entonces, en mi ejemplo, cada cadena vacía primero se valida para ver si no hay "hede" más adelante, antes de que un personaje sea consumido por el . (punto). La expresión regular (?!hede). lo hará 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 se anclan para asegurarse de que se consume toda la entrada: ^((?!hede).)*$

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


4852



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 Contiene "Hede":

^((?!hede).)*$

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


603



Si solo lo estás usando para grep, puedes 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 quería decir con "opciones de herramientas".


163



Responder:

^((?!hede).)*$

Explicación:

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

hedetu hilo,

) fin de mirar hacia adelante, . cualquier caracter excepto \ n,
)* end of \ 1 (Nota: como 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 cadena


121



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

Expresiones regulares en el significado de las ciencias informáticas teóricas NO PUEDEN Hazlo asi. Para ellos tenía que parecerse a esto:

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

Esto solo hace un partido COMPLETO. Hacerlo para sub-matches sería incluso más incómodo.


89



Si quieres la prueba de expresiones regulares para solamente fallar si el cadena completa coincidencias, lo siguiente funcionará:

^(?!hede$).*

p.ej. - 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 fuera de la prueba si necesita alguna característica regex (aquí, insensibilidad de mayúsculas y minúsculas):

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

La solución de expresiones regulares en la parte superior puede ser útil, sin embargo, en situaciones donde se requiere una prueba de expresiones regulares positiva (tal vez por una API).


48



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


47



FWIW, dado que los lenguajes regulares (también conocidos como idiomas racionales) se cierran bajo la 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).

Primero defines el tipo de tus 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, falserechazado

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 ingresas tu expresión:

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

convierte esta expresión a un autómata:

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

The corresponding automaton

finalmente, convierte este autómata a 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 se escribe . (cualquier personaje). Entonces, con un poco de reescritura ()|h(ed?)?|([^h]|h([^e]|e([^d]|d([^e]|e.)))).*.

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


43



Puntos de referencia

Decidí evaluar algunas de las Opciones presentadas y comparar su rendimiento, así como utilizar algunas características nuevas. Benchmarking 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 carreras: 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

Como .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))*$ lo que resulta en una pequeña ganancia de rendimiento.

Pero la solución más rápida y más legible para el rendimiento parece ser 05 usando un enunciado condicional o 04 con el cuantificador posesivo. Creo que las soluciones de Perl deberían ser aún más rápidas y fáciles de leer.


38



Con un lookaid negativo, 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 más adelante al unir cualquier personaje individual. Podemos evitar esto y dejar que la parte de anticipación revise todo el texto, asegúrese de que no haya 'hede', y luego la parte normal (. *) Puede comer todo el texto de una sola vez.

Aquí está la expresión regular mejorada:

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

Tenga en cuenta que el cuantificador perezoso (*?) En la parte negativa de anticipación es opcional, puede usar (*) cuantificador codicioso en su lugar, dependiendo de sus datos: si 'hede' sí lo está y al principio la mitad 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 se presenta, ambos serían igual de lentos.

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

Para obtener más información acerca de la búsqueda anticipada, consulte el excelente artículo: Dominar el mirar hacia adelante y mirar hacia atrás.

Además, echa un vistazo RegexGen.js, un generador de expresiones regulares de JavaScript que ayuda a construir expresiones regulares complejas. Con RegexGen.js, puedes 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()
);

37



No regex, pero me pareció 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-

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 grep serial es (no es un comentario) y (coincide con dir)


30