Pregunta

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 él " ;, así:

^((?!foo).)*$

¿Se puede lograr lo mismo sin tener que mirar a todos lados (las preocupaciones de complejidad y rendimiento se dejan de lado por el momento)?

¿Fue útil?

Solución

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


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

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

La expresión regular supone que cada línea termina con un carácter de nueva línea si no es así, vea las expresiones regulares de C ++ y grep.

Los programas de muestra en Perl, Python, C ++ y grep tienen 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])*
    #include <iostream>
    #include <string>
    #include <boost/regex.hpp>
    
    int main()
    {
      boost::regex re("^(f(o([^o]|$)|([^o]|$))|[^f])*
    $ grep "^\(f\(o\([^o]\|$\)\|\([^o]\|$\)\)\|[^f]\)*
    foo
    'foo'
    abdfoode
    abdfode
    abdfde
    abcde
    f
    
    fo
    foo
    fooo
    ofooa
    ofo
    ofoo
    
    quot; in.txt
    quot;); //NOTE: "|
    abdfode
    abdfde
    abcde
    f
    
    fo
    ofo
    
    quot;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; }
    quot;) for line in ifilter(re_not_foo.match, fileinput.input()): sys.stdout.write(line)
  • c ++

    <*>
  • grep

    <*>

Archivo de muestra:

<*>

Salida:

<*>

Otros consejos

Encontré esta pregunta y tomé el hecho de que no había una expresión regular en pleno funcionamiento 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 sabores que permitan la agrupación atómica pero no busquen, pero la Pregunta preguntó si es posible en regex establecer una exclusión sin buscar, y < em> 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 alguna razón, puede usar la agrupación atómica pero no cuantificadores posesivos ni buscar, puede usar:

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

Sin embargo, como otros señalan, probablemente sea más práctico negar una coincidencia 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 desea validar que una cadena contiene solo ciertos caracteres.

Podrías escribir eso así:

^[A-Za-z0-9.$-font>*$

y acepte un resultado true como válido, o así:

[^A-Za-z0-9.$-font>

y acepte 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 programa, por ejemplo. Pero vale la pena recordarlo. Su problema específico, por ejemplo, la expresión es mucho más simple si puede usar la negación como esta.

Me topé 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 a esta situación: Por ejemplo, "cada línea que no tiene" foo " en él " era simplemente usar la opción de sentido de coincidencia -v invertido en grep.

grep -v foo

esto devuelve todas las líneas en un archivo que no coinciden con 'foo'

Es tan simple que tengo la fuerte sensación de haber leído mal tu pregunta ...

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top