Regex: Emparejamiento por exclusión, sin mirar hacia adelante – ¿es posible?

En algunos sabores de expresiones regulares, las aserciones [negativas] de ancho cero (mirar hacia adelante / mirar hacia atrás) no son compatibles.

Esto hace que sea extremadamente difícil (¿imposible?) Establecer una exclusión. Por ejemplo, “cada línea que no tiene” foo “en ella”, como esta:

^((?!foo).)*$ 

¿Se puede lograr lo mismo sin utilizar la mirada en todo (preocupaciones de complejidad y rendimiento dejadas de lado por el momento)?

ACTUALIZACIÓN: falla “con dos ff antes de oo”, como señaló @Ciantic en los comentarios.


 ^(f(o[^o]|[^o])|[^f])*$ 

NOTA: es mucho más fácil simplemente negar una coincidencia en el lado del cliente en lugar de usar la expresión regular anterior.

La expresión regular asume que cada línea termina con una char de nueva línea si no es así y luego ve las expresiones regulares de C ++ y grep.

Los progtwigs de muestra en Perl, Python, C ++ y grep proporcionan el mismo resultado.

  • Perl

     #!/usr/bin/perl -wn print if /^(f(o[^o]|[^o])|[^f])*$/; 
  • python

     #!/usr/bin/env python import fileinput, re, sys from itertools import ifilter re_not_foo = re.compile(r"^(f(o[^o]|[^o])|[^f])*$") for line in ifilter(re_not_foo.match, fileinput.input()): sys.stdout.write(line) 
  • c ++

     #include  #include  #include  int main() { boost::regex re("^(f(o([^o]|$)|([^o]|$))|[^f])*$"); //NOTE: "|$"s are there due to `getline()` strips newline char std::string line; while (std::getline(std::cin, line)) if (boost::regex_match(line, re)) std::cout << line << std::endl; } 
  • grep

     $ grep "^\(f\(o\([^o]\|$\)\|\([^o]\|$\)\)\|[^f]\)*$" in.txt 

Archivo de muestra:

 foo 'foo' abdfoode abdfode abdfde abcde f fo foo fooo ofooa ofo ofoo 

Salida:

 abdfode abdfde abcde f fo ofo 

Me encontré con esta pregunta y tomé el hecho de que no había una expresión regular que funcionara como un desafío personal. Creo que he logrado crear una expresión regular que funciona para todas las entradas, siempre que pueda usar agrupación atómica / cuantificadores posesivos .

Por supuesto, no estoy seguro de si hay algún tipo de sabores que permitan la agrupación atómica, pero no la búsqueda, pero la pregunta preguntó si es posible en expresiones regulares express una exclusión sin un vistazo, y es técnicamente posible:

 \A(?:$|[^f]++|f++(?:[^o]|$)|(?:f++o)*+(?:[^o]|$))*\Z 

Explicación:

 \A #Start of string (?: #Non-capturing group $ #Consume end-of-line. We're not in foo-mode. |[^f]++ #Consume every non-'f'. We're not in foo-mode. |f++(?:[^o]|$) #Enter foo-mode with an 'f'. Consume all 'f's, but only exit foo-mode if 'o' is not the next character. Thus, 'f' is valid but 'fo' is invalid. |(?:f++o)*+(?:[^o]|$) #Enter foo-mode with an 'f'. Consume all 'f's, followed by a single 'o'. Repeat, since '(f+o)*' by itself cannot contain 'foo'. Only exit foo-mode if 'o' is not the next character following (f+o). Thus, 'fo' is valid but 'foo' is invalid. )* #Repeat the non-capturing group \Z #End of string. Note that this regex only works in flavours that can match $\Z 

Si, por el motivo que sea, puede utilizar la agrupación atómica pero no los cuantificadores posesivos ni las alternativas, puede usar:

 \A(?:$|(?>[^f]+)|(?>f+)(?:[^o]|$)|(?>(?:(?>f+)o)*)(?:[^o]|$))*\Z 

Como otros señalan, sin embargo, es más práctico simplemente negar un partido por otros medios.

Por lo general, puede buscar foo e invertir el resultado de la coincidencia de expresiones regulares del código del cliente.

Para un ejemplo simple, supongamos que quiere validar que una cadena contiene solo ciertos caracteres.

Podrías escribir eso así:

^[A-Za-z0-9.$-]*$

y acepta un resultado true como válido, o como este:

[^A-Za-z0-9.$-]

y acepta un resultado false como válido.

Por supuesto, esto no siempre es una opción: a veces solo tienes que poner la expresión en un archivo de configuración o pasarla a otro progtwig, por ejemplo. Pero vale la pena recordar Su problema específico, por ejemplo, la expresión es mucho más simple si puede usar una negación como esta.

Me encontré con esta pregunta buscando mi propia solución de exclusión de expresiones regulares, donde estoy tratando de excluir una secuencia dentro de mi expresión regular.

Mi reacción inicial ante esta situación: por ejemplo, “cada línea que no tiene” foo “en ella” era simplemente para usar la opción -v invertir el sentido de coincidencia en grep.

 grep -v foo 

esto devuelve todas las líneas en un archivo que no coincide con ‘foo’

Es tan simple que tengo la fuerte sensación de que acabo de leer mal tu pregunta …

    Intereting Posts