2010-01-04 16 views
10

Supongamos que tengo un archivo que contiene las líneas que estoy tratando de igualar en contra:¿Cómo puedo escapar de los metacaracteres cuando interpolar una variable en el operador de coincidencia de Perl?

foo 
quux 
bar 

En mi código, tengo otra matriz:

foo 
baz 
quux 

Digamos que iterar a través del archivo, llamando a cada elemento $word, y la lista interna que estamos verificando, @arr.

if(grep {$_ =~ m/^$word$/i} @arr) 

Esto funciona correctamente, pero en el algo posible caso en el que tenemos un caso de prueba de fo. en el archivo, el . funciona como un operador de comodín en la expresión regular, y fo. continuación partidos foo, lo cual no es aceptable .

Esto es, por supuesto, porque Perl está interpolando la variable en una expresión regular.

La pregunta:

¿Cómo fuerzo Perl se usa la variable literalmente?

+0

Ver http://stackoverflow.com/questions/1949731/how-can-i-escape-a-literal-string-i- want-to-interpolate-into-a-regular-expression –

+1

posible duplicado de [¿Cómo manejo los caracteres especiales en un Perl regex?] (http://stackoverflow.com/questions/576435/how-do-i-handle -special-characters-in-a-perl-regex) – daxim

Respuesta

10

La respuesta correcta es - no utilizar expresiones regulares. No digo que las expresiones regulares sean malas, pero usarlas para (lo que es igual a) la simple comprobación de igualdad es excesiva.

Use: grep { lc($_) eq lc($word) } @arr y sea feliz.

+2

Buen punto. La solución de expresiones regulares es un remanente de código más antiguo y más complicado. –

30

Uso \Q...\E para escapar de símbolos especiales directamente en la cadena de valor de la variable Perl después de la interpolación:

if(grep {$_ =~ m/^\Q$word\E$/i} @arr) 
+0

¿Qué pasa si '$ word = 'fo \ E.''? –

+0

Entonces regexp será algo así como "m/^ fo \\ E \. $/I". Consulte la descripción del meta-símbolo '\ Q' en http://perldoc.perl.org/perlfaq6.html –

+0

Específicamente, http://perldoc.perl.org/perlop.html#Quote-and-Quote-like-Operators notes que '" abc \ Qfoo \ tbar $ s \ Exyz "' es equivalente a '" abc ". quotemeta ("foo \ tbar $ s"). "xyz" '. –

15

De respuesta perlfaq6 's a How do I match a regular expression that's in a variable?:


No tenemos los patrones de codificar directamente en el operador partido (o cualquier otra cosa que funciona con expresiones regulares). Podemos poner el patrón en una variable para su uso posterior.

El operador de coincidencia es un contexto de comillas dobles, por lo que puede interpolar su variable como una cadena entre comillas dobles. En este caso, lee la expresión regular como entrada de usuario y la almacena en $ regex. Una vez que tenga el patrón en $ regex, use esa variable en el operador de coincidencia.

chomp(my $regex = <STDIN>); 

if($string =~ m/$regex/) { ... } 

Los caracteres especiales de expresiones regulares en $ expresiones regulares son todavía especial, y el patrón aún tiene que ser válido o Perl se quejan. Por ejemplo, en este patrón hay un paréntesis sin par.

my $regex = "Unmatched (paren"; 

"Two parens to bind them all" =~ m/$regex/; 

Cuando Perl compila la expresión regular, trata el paréntesis como el inicio de una coincidencia de memoria.Cuando no encuentra el paréntesis de cierre, se queja:

Unmatched (in regex; marked by <-- HERE in m/Unmatched (<-- HERE paren/ at script line 3. 

Usted puede evitar esto de varias maneras dependiendo de nuestra situación. Primero, si no desea que ninguno de los caracteres de la cadena sea especial, puede escapar de ellos con quotemeta antes de usar la cadena.

chomp(my $regex = <STDIN>); 
$regex = quotemeta($regex); 

if($string =~ m/$regex/) { ... } 

También puede hacerlo directamente en el operador de coincidencia utilizando las secuencias \ Q y \ E. La \ Q le dice a Perl dónde comenzar a escapar caracteres especiales, y la \ E le dice dónde detenerse (ver perlop para más detalles).

chomp(my $regex = <STDIN>); 

if($string =~ m/\Q$regex\E/) { ... } 

Alternativamente, puede utilizar QR //, el operador regular de cotización expresión (ver perlop para más detalles). Cita y tal vez compila el patrón y puede aplicar indicadores de expresión regulares al patrón.

chomp(my $input = <STDIN>); 

my $regex = qr/$input/is; 

$string =~ m/$regex/ # same as m/$input/is; 

También puede atrapar cualquier error al envolver un bloque eval alrededor de todo.

chomp(my $input = <STDIN>); 

eval { 
    if($string =~ m/\Q$input\E/) { ... } 
    }; 
warn [email protected] if [email protected]; 

O ...

my $regex = eval { qr/$input/is }; 
if(defined $regex) { 
    $string =~ m/$regex/; 
    } 
else { 
    warn [email protected]; 
    } 
+0

¿La evaluación en el segundo ejemplo del último código intercepta errores en "{...}" o podría haber algo mal en "if ($ string = ~ m/\ Q $ input \ E /)" también? –

+0

La evaluación detectará todos los errores en su bloque, pero en términos de esta pregunta se está detectando un error en el código explícito que se ve en el operador de coincidencia. –

2

No creo que desee una expresión regular en este caso, ya que no son coincidentes con un patrón. Estás buscando una secuencia literal de personajes que ya conozcas. Construir un hash con los valores para que coincidan y usarlo para filtrar @arr:

open my $fh, '<', $filename or die "..."; 
my %hash = map { chomp; lc($_), 1 } <$fh>; 

foreach my $item (@arr) 
     { 
     next unless exists $hash{ lc($item) }; 
     print "I matched [$item]\n"; 
     } 
Cuestiones relacionadas