Cómo negar palabra específica en expresiones regulares?

Sé que puedo negar el grupo de caracteres como en [^bar] pero necesito una expresión regular donde la negación se aplica a la palabra específica, por lo que en mi ejemplo, ¿cómo niego una "bar" real y no "any chars in bar" ?

Una buena forma de hacerlo es utilizar un lookahead negativo :

 ^(?!.*bar).*$ 

A menos que el rendimiento sea una preocupación máxima, a menudo es más fácil simplemente ejecutar los resultados a través de un segundo pase, omitiendo aquellos que coinciden con las palabras que desea negar.

Las expresiones regulares generalmente significan que está haciendo scripting o algún tipo de tarea de bajo rendimiento de todos modos, así que encuentre una solución que sea fácil de leer, fácil de entender y fácil de mantener.

La siguiente expresión regular hará lo que usted desee (siempre que soporte look-back negativo y lookaheads son compatibles), haciendo coincidir las cosas correctamente; el único problema es que coincide con caracteres individuales (es decir, cada coincidencia es un solo carácter en lugar de todos los caracteres entre dos “barras” consecutivas), lo que posiblemente genere un gasto elevado si está trabajando con cadenas muy largas.

 b(?!ar)|(?< !b)a|a(?!r)|(? 

Puede usar un look-ahead negativo o un look-behind :

 ^(?!.*?bar).* ^(.(?< !bar))*?$ 

O usa solo lo básico:

 ^(?:[^b]+|b(?:$|[^a]|a(?:$|[^r])))*$ 

Todos estos coinciden con cualquier cosa que no contenga bar .

Encontré este hilo del foro mientras trataba de identificar una expresión regular para la siguiente statement en inglés:

Dada una cadena de entrada, empareja todo a menos que esta cadena de entrada sea exactamente ‘barra’; por ejemplo, quiero hacer coincidir ‘barrera’ y ‘barra’, así como ‘foo’.

Aquí está la expresión regular que se me ocurrió

 ^(bar.+|(?!bar).*)$ 

Mi traducción al inglés de la expresión regular es “hacer coincidir la cadena si comienza con ‘barra’ y tiene al menos otro carácter, o si la cadena no comienza con ‘barra’.

Solución:

 ^(?!.*STRING1|.*STRING2|.*STRING3).*$ 

xxxxxx OK

xxxSTRING1xxx KO (es si se desea)

xxxSTRING2xxx KO (es si se desea)

xxxSTRING3xxx KO (es si se desea)

La respuesta aceptada es buena, pero realmente es una solución para la falta de un operador de negación de sub-expresión simple en expresiones regulares. Esta es la razón por la que grep --invert-match sale. Por lo tanto, en * nixes, puede lograr el resultado deseado utilizando tuberías y una segunda expresión regular.

 grep 'something I want' | grep --invert-match 'but not these ones' 

Sigue siendo una solución, pero tal vez más fácil de recordar.

Espero completar la respuesta

Como Chris especificó Regex Tutorial es un mejor recurso para aprender Regex.

Sin embargo, realmente consumió tiempo para leer.

Hago una trampa para la conveniencia mnemotécnica.
[] , () , {} liderando cada clase que es fácil de recordar.

 Regex = {'single_character': ['[]', '.', {'negate':'^'}], 'capturing_group' : ['()', '|', '\\', 'backreferences and named group'], 'repetition' : ['{}', '*', '+', '?', 'greedy vs lazy'], 'anchor' : ['^', '\b', '$'], 'non_printable' : ['\n', '\t', '\r', '\f', '\v'], 'shorthand' : ['\d', '\w', '\s'], } 

Solo pensé en otra cosa que podría hacerse. Es muy diferente de mi primera respuesta, ya que no usa expresiones regulares, así que decidí hacer una segunda respuesta.

Utilice el método de split() su idioma de elección split() equivalente en la cadena con la palabra a negar como argumento para qué dividir. Un ejemplo usando Python:

 >>> text = 'barbarasdbarbar 1234egb ar bar32 sdfbaraadf' >>> text.split('bar') ['', '', 'asd', '', ' 1234egb ar ', '32 sdf', 'aadf'] 

Lo bueno de hacerlo de esta manera, al menos en Python (no recuerdo si la funcionalidad sería la misma en, digamos, Visual Basic o Java), es que te permite saber indirectamente cuando se repitió “barra” en la cadena debido al hecho de que las cadenas vacías entre “barras” se incluyen en la lista de resultados (aunque la cadena vacía al principio se debe a que hay una “barra” al comienzo de la cadena). Si no quiere eso, simplemente puede eliminar las cadenas vacías de la lista.

Tenía una lista de nombres de archivo y quería excluir algunos, con este tipo de comportamiento (Ruby):

 files = [ 'mydir/states.rb', # don't match these 'countries.rb', 'mydir/states_bkp.rb', # match these 'mydir/city_states.rb' ] excluded = ['states', 'countries'] # set my_rgx here result = WankyAPI.filter(files, my_rgx) # I didn't write WankyAPI... assert result == ['mydir/city_states.rb', 'mydir/states_bkp.rb'] 

Aquí está mi solución:

 excluded_rgx = excluded.map{|e| e+'\.'}.join('|') my_rgx = /(^|\/)((?!#{excluded_rgx})[^\.\/]*)\.rb$/ 

Mis suposiciones para esta aplicación:

  • La cadena que se va a excluir está al comienzo de la entrada, o inmediatamente después de una barra inclinada.
  • Las cadenas permitidas terminan con .rb .
  • Los nombres de archivos permitidos no tienen un . personaje antes del .rb .