2010-01-04 2 views
10

Supposons que j'ai un fichier contenant des lignes que je suis en train de correspondre:Comment puis-je échapper des méta-caractères lorsque j'interpole une variable dans l'opérateur de correspondance de Perl?

foo 
quux 
bar 

Dans mon code, j'ai un autre tableau:

foo 
baz 
quux 

Supposons que nous parcourons le fichier, appelant chaque élément $word, et la liste interne que nous vérifions, @arr.

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

Cela fonctionne correctement, mais dans le cas peu possible où nous avons un cas de test de fo. dans le fichier, le . fonctionne comme un opérateur générique dans le regex et fo. correspond alors foo, ce qui est inacceptable .

Ceci est bien sûr dû au fait que Perl interpole la variable dans une regex.

La question:

Comment forcer Perl à utiliser littéralement la variable?

+0

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

+1

duplication possible de [Comment gérer les caractères spéciaux dans une regex Perl?] (http://stackoverflow.com/questions/576435/how-do-i-handle -special-characters-in-a-perl-regex) – daxim

Répondre

10

La bonne réponse est - ne pas utiliser les expressions régulières. Je ne dis pas que les expressions rationnelles sont mauvaises, mais les utiliser pour (ce qui équivaut à) vérifier l'égalité simple est exagéré.

Utilisez: grep { lc($_) eq lc($word) } @arr et soyez heureux.

+2

Bon point. La solution regex est un vestige de code plus ancien et plus compliqué. –

30

Utilisez \Q...\E pour échapper à des symboles spéciaux directement dans la chaîne perl après interpolation de valeur variable:

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

Que faire si '$ word = 'fo \ E.''? –

+0

Alors regexp sera quelque chose comme "m/^ fo \\ E \. $/I". Voir la description du méta-symbole '\ Q' sur http://perldoc.perl.org/perlfaq6.html –

+0

Plus précisément, http://perldoc.perl.org/perlop.html#Quote-and-Quote-like-Operators notes que '" abc \ Qfoo \ tbar $ s \ Exyz "' est équivalent à '" abc ". quotemeta ("foo \ tbar $ s"). "xyz" '. –

15

De la réponse de perlfaq6 à How do I match a regular expression that's in a variable?:


Nous n'avons pas à des modèles difficiles codes dans l'opérateur de correspondance (ou toute autre chose qui fonctionne avec des expressions régulières). Nous pouvons mettre le motif dans une variable pour une utilisation ultérieure. L'opérateur match est un contexte de double guillemets, de sorte que vous pouvez interpoler votre variable comme une chaîne entre guillemets. Dans ce cas, vous lisez l'expression régulière en tant qu'entrée utilisateur et la stockez dans $ regex. Une fois que vous avez le motif dans $ regex, vous utilisez cette variable dans l'opérateur match.

chomp(my $regex = <STDIN>); 

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

Toute expression régulière des caractères spéciaux $ regex sont toujours spéciales, et le modèle doit encore être valide ou Perl se plaindra. Par exemple, dans ce modèle, il y a une parenthèse non appariée.

my $regex = "Unmatched (paren"; 

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

Lorsque Perl compile l'expression régulière, il traite la parenthèse comme le début d'un match de mémoire.Quand il ne trouve pas la parenthèse fermante, il se plaint:

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

Vous pouvez contourner ce de plusieurs façons en fonction de notre situation. Tout d'abord, si vous ne voulez pas que les caractères de la chaîne soient spéciaux, vous pouvez leur échapper avec quotemeta avant d'utiliser la chaîne. Vous pouvez également le faire directement dans l'opérateur match en utilisant les séquences \ Q et \ E. Le \ Q indique à Perl où commencer à échapper des caractères spéciaux, et le \ E lui indique où s'arrêter (voir perlop pour plus de détails).

chomp(my $regex = <STDIN>); 

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

Alternativement, vous pouvez utiliser // qr, l'opérateur de citation d'expression régulière (voir perlop pour plus de détails). Il cite et compile peut-être le motif, et vous pouvez appliquer des drapeaux d'expression régulière au motif.

chomp(my $input = <STDIN>); 

my $regex = qr/$input/is; 

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

Vous pourriez également vouloir piéger des erreurs en enveloppant un bloc eval autour de l'ensemble.

chomp(my $input = <STDIN>); 

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

Ou ...

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

Est-ce que eval dans le deuxième exemple de code dernier pour piéger les erreurs dans "{...}" ou pourrait-il y avoir quelque chose de faux dans "if ($ string = ~ m/\ Q $ input \ E /)" aussi? –

+0

L'eval va attraper toutes les erreurs dans son bloc, mais en termes de cette question, il attrape une erreur dans le code explicite que vous voyez dans l'opérateur match. –

2

Je ne pense pas que vous voulez un regex dans ce cas, puisque vous n'êtes pas correspondant à un motif. Vous recherchez une séquence littérale de caractères que vous connaissez déjà. Construire un hachage avec les valeurs pour correspondre et l'utiliser pour filtrer @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"; 
     } 
Questions connexes