Saltar al contenido

¿Cómo hacer un grep insensible al acento?

Solución:

Está buscando un montón de clases de equivalencia de expresiones regulares POSIX:

14.3.6.2 Operadores de clase de equivalencia ([= … =])

    Regex reconoce expresiones de clases de equivalencia dentro de listas. A expresión de clase de equivalencia es un conjunto de elementos de clasificación que pertenecen todos a la misma clase de equivalencia. Formas una expresión de clase de equivalencia poniendo un elemento de clasificación entre un operador de clase de equivalencia abierta y un operador de clase de equivalencia cercana. [= represents the open-equivalence-class operator and =] representa el operador de clase de equivalencia cercana. Por ejemplo, si a y A eran una clase de equivalencia, entonces ambos [[=a=]] y [[=A=]] coincidiría con ambos a y A. Si el elemento de clasificación en una expresión de clase de equivalencia no es parte de una clase de equivalencia, entonces el comparador considera que la expresión de la clase de equivalencia es un símbolo de clasificación.

Estoy usando signos de intercalación en la siguiente línea para indicar lo que realmente está coloreado. También modifiqué la cadena de prueba para ilustrar un punto sobre el caso.

$ echo "I match àei but also äēì and possibly æi" | grep '[[=a=]][[=e=]][[=i=]]'
I match àei but also äēì and possibly æi
        ^^^          ^^^

Esto coincide con todas las palabras como aei. El hecho de que no coincide æi debería ser un recordatorio de que está en deuda con cualquier mapeo que exista en la biblioteca de expresiones regulares que está usando (presumiblemente gnulib, que es lo que vinculé y cité), aunque creo que es bastante probable que los dígrafos estén más allá del alcance incluso de los mejor mapa de clases de equivalencia.

No debe esperar que las clases de equivalencia sean portátiles ya que son demasiado arcanos.


Llevando esto un paso más allá, si SOLO quieres caracteres acentuados, las cosas se complican mucho más. Aquí he cambiado tu solicitud de aei dentro [aei].

$ echo "I match àei but also äēì and possibly æi" | grep '[[=a=][=e=][=i=]]'
I match àei but also äēì and possibly æi
^  ^    ^^^     ^    ^^^ ^       ^     ^

Limpiar esto para evitar coincidencias sin acento requeriría tanto clases de equivalencia como mirar adelante / mirar atrás, y aunque BRE (expresión regular POSIX básica) y ERE (expresión regular POSIX extendida) admiten la primera, ambos carecen de la última. Libpcre (la biblioteca C para expresiones regulares compatibles con perl que grep -P y la mayoría de los demás usan) y perl apoyan el último pero carecen del primero:

Prueba el n. ° 1: grep con libpcre: falla

$ echo "I match àei but also äēì and possibly æi" 
    | grep -P '[[=a=][=e=][=i=]](?<![aei])'
grep: POSIX collating elements are not supported

Prueba el n. ° 2: perl sí mismo: falla

$ echo "I match àei but also äēì and possibly æi" 
    | perl -ne 'print if /[[=a=][=e=][=i=]](?<![aei])/'
POSIX syntax [= =] is reserved for future extensions in regex; marked by <-- HERE in m/[[=a=][=e= <-- HERE ][=i=]](?<![aei])/ at -e line 1.

Prueba el n. ° 3: python (que tiene su propia implementación PCRE): (silencioso) fracaso

$ echo "I match àei but also äēì and possibly æi" 
    | python -c 'import re, sys;
                 print re.findall(r"[[=a=][=e=][=i=]]", sys.stdin.read())'
[]

Wow, una característica de expresiones regulares que PCRE, python, e incluso perl no ¡apoyo! No hay demasiados de esos. (No importa que la queja esté en el segundo clase de equivalencia, todavía se queja dado solo /[[=a=]]/.) Esto es una prueba más de que las clases de equivalencia son arcanas.

De hecho, parece que no hay alguna Bibliotecas PCRE capaces de clases de equivalencia; la sección sobre clases de equivalencia en regular-expressions.info afirma que solo las bibliotecas de expresiones regulares que implementan el estándar POSIX realmente tienen este soporte. ÑU grep se acerca ya que puede hacer BRE, ERE y PCRE, pero no puede combinarlos.

Entonces lo haremos en dos partes.

Prueba # 4: truco repugnante: éxito

$ echo "I match àei but also äēì and possibly æi" 
    | grep --color=always '[[=a=][=e=][=i=]]' 
    | perl -pne "s/e[[0-9;]*me[K(?i)([aei])/$1/g"
I match àei but also äēì and possibly æi
        ^            ^^^

Paseo por el código:

  • grep fuerza el color para que perl puede teclear los códigos de color para anotar las coincidencias
  • ${GREP_COLOR:-01;31} notas grepcolor de (con el mismo rojo brillante predeterminado)
  • perl‘s s/// El comando coincide con el código de color completo y luego con las letras sin acentos que queremos eliminar de los resultados finales. Reemplaza todo eso con las letras (sin color)
  • Cualquier cosa después (?i) en el perl regex no distingue entre mayúsculas y minúsculas ya que [[=i=]] partidos I
  • perl -p imprime cada línea de su entrada al completar su -e ejecución

Para obtener más información sobre BRE vs ERE vs PCRE y otros, consulte esta publicación de expresiones regulares de StackExchange o las expresiones regulares de POSIX en regular-expressions.info. Para obtener más información sobre las diferencias por idioma (incluido libpcre vs python PCRE vs perl), consulte las herramientas en regular-expressions.info.


Actualizaciones de 2019: GNU Grep ahora usa $GREP_COLORS que puede verse como ms=1;41 que tiene prioridad sobre los mayores $GREP_COLOR igual que 1;41. Esto es más difícil de extraer (y es difícil hacer malabares entre los dos), así que modifiqué el código de Perl en el intento # 4 para buscar alguna Código de color SGR en lugar de teclear solo el color que agregaría grep. Consulte la revisión 2 de esta respuesta para el código anterior.

Actualmente no puedo verificar si BSD grep, que es utilizado por Apple Mac OS X, admite clases de equivalencia de expresiones regulares POSIX.

No creo que esto se pueda hacer en grep, a menos que esté dispuesto a escribir un script de shell que use iconv y diff, que sería un poco diferente visualmente de lo que solicita.

Aquí hay algo muy parecido a su solicitud a través de un script de perl rápido:

#!/usr/bin/perl
# tgrep 0.1 Copyright 2014 by Adam Katz, GPL version 2 or later

use strict;
use warnings;
use open qw(:std :utf8);
use Text::Unidecode;

my $regex = shift or die "Missing pattern.nUsage: tgrep PATTERN [FILE...]";

my $retval = 1;  # default to false (no hits)

while(<>) {
  my $line = "", my $hit = 0;
  while(/G(S*(?:s+|$))/g){             # for each word (w/ trailing spaces)
    my $word = $1;
    if(unidecode($word) =~ qr/$regex/) {  # if there was a match
      $hit++;                             # note that fact
      $retval = 0;                        # final exit code will be 0 (true)
      $line .= "e[1;31m$worde[0;0m";    # display word in RED
    } else {
      $line .= $word;                     # display non-matching word normally
    }
  }
  print $line if $hit;                    # only display lines with matches
}

exit $retval;

Markdown no me permite hacer texto rojo, así que aquí está el resultado con los hits entre comillas:

$ echo "match àei but also äēì and possibly æi" | tgrep aei
match "àei" but also "äēì" and possibly "æi"

Esto resaltará la coincidencia palabras en lugar de la coincidencia real, lo que sería muy difícil de hacer sin crear clases de caracteres masivas y / o componer un analizador de expresiones regulares por partes. Por lo tanto, buscar el patrón “ae” en lugar de “aei” produciría los mismos resultados (en este caso).

Ninguna de las banderas de grep se replica en este ejemplo de juguete. Quería mantenerlo simple.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *