当我在Perl的匹配运算符中插入变量时,如何转义元字符?

时间:2010-01-04 18:16:06

标签: regex perl variables interpolation

假设我有一个包含我想要匹配的行的文件:

foo
quux
bar

在我的代码中,我有另一个数组:

foo
baz
quux

假设我们遍历文件,调用每个元素$word,以及我们正在检查的内部列表@arr

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

这是正常的,但在某种情况下,我们在文件中有fo.的测试用例,.在正则表达式中作为通配符运算符运行,fo.然后匹配foo,这是不可接受的。

这当然是因为Perl正在将变量插入到正则表达式中。

问题:

如何强制Perl按字面意思使用变量?

5 个答案:

答案 0 :(得分:33)

使用\Q...\E在变量值插值后直接在perl字符串中转义特殊符号:

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

答案 1 :(得分:17)

perlfaq6回答How do I match a regular expression that's in a variable?


我们不必将模式硬编码到匹配运算符(或其他任何与正则表达式一起使用的代码)中。我们可以将模式放在变量中供以后使用。

匹配运算符是双引号上下文,因此您可以像双引号字符串一样插入变量。在这种情况下,您将正则表达式作为用户输入读取并将其存储在$ regex中。在$ regex中使用该模式后,可以在匹配运算符中使用该变量。

chomp( my $regex = <STDIN> );

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

$ regex中的任何正则表达式特殊字符仍然是特殊的,并且该模式仍然必须有效或Perl会抱怨。例如,在这种模式中有一个不成对的括号。

my $regex = "Unmatched ( paren";

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

当Perl编译正则表达式时,它将括号视为内存匹配的开始。当它没有找到右括号时,它会抱怨:

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

根据我们的情况,您可以通过多种方式解决这个问题。首先,如果您不希望字符串中的任何字符都是特殊字符,则可以在使用字符串之前使用quotemeta对它们进行转义。

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

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

您也可以使用\ Q和\ E序列在匹配运算符中直接执行此操作。 \ Q告诉Perl从哪里开始转义特殊字符,\ E告诉它在哪里停止(有关详细信息,请参阅perlop)。

chomp( my $regex = <STDIN> );

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

或者,您可以使用qr //,正则表达式引用运算符(有关详细信息,请参阅perlop)。它引用并可能编译模式,您可以将正则表达式标志应用于模式。

chomp( my $input = <STDIN> );

my $regex = qr/$input/is;

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

您可能还希望通过在整个事物周围包装一个eval块来捕获任何错误。

chomp( my $input = <STDIN> );

eval {
    if( $string =~ m/\Q$input\E/ ) { ... }
    };
warn $@ if $@;

或者...

my $regex = eval { qr/$input/is };
if( defined $regex ) {
    $string =~ m/$regex/;
    }
else {
    warn $@;
    }

答案 2 :(得分:12)

正确的答案是 - 不要使用正则表达式。我不是说正则表达式很糟糕,但是使用它们(等于)简单的相等检查是过度的。

使用:grep { lc($_) eq lc($word) } @arr并且快乐。

答案 3 :(得分:5)

<强> Quotemeta

返回EXPR的值,并将所有非“word”字符反斜杠。

http://perldoc.perl.org/functions/quotemeta.html

答案 4 :(得分:2)

在这种情况下,我认为你不想要一个正则表达式,因为你没有匹配一个模式。您正在寻找您已经知道的文字字符序列。使用要匹配的值构建哈希,并使用它来过滤@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";
      }