如何在perl中将变量用作正则表达式修饰符?

时间:2014-12-20 02:51:01

标签: regex perl dynamic case-sensitive modifier

我正在编写一个抽象函数,它会询问用户一个给定的问题并根据给定的正则表达式验证答案。重复该问题,直到答案与验证正则表达式匹配。但是,我还希望客户端能够指定答案是否必须符合大小写。所以像这样:

sub ask {
    my ($prompt, $validationRe, $caseSensitive) = @_;
    my $modifier = ($caseSensitive) ? "" : "i";
    my $ans;
    my $isValid;

    do {
        print $prompt;
        $ans = <>;
        chomp($ans);

        # What I want to do that doesn't work:
        # $isValid = $ans =~ /$validationRe/$modifier;

        # What I have to do:
        $isValid = ($caseSensitive) ?
            ($ans =~ /$validationRe/) :
            ($ans =~ /$validationRe/i);

    } while (!$isValid);

    return $ans;
}

Upshot:有没有办法动态指定正则表达式的修饰符?

4 个答案:

答案 0 :(得分:11)

  

Upshot:有没有办法动态指定正则表达式的修饰符?

来自perldoc perlre

  

“(?adlupimsx-imsx)”   “(?^ alupimsx)”       要打开的一个或多个嵌入式模式匹配修饰符(或       关闭,如果前面加“ - ”)对于模式的其余部分或       封闭模式组的其余部分(如果有的话)。

     

这对于动态模式尤其有用,例如读取的模式       从配置文件中获取,从参数中获取或在中指定       某个地方的桌子。考虑一些模式想要的情况       区分大小写,有些则不区分大小写:不区分大小写的仅需要       在模式的前面加上“(?i)”。

这给了你一些

的内容
$isValid = $ans =~ m/(?$modifier)$validationRe/;

在以这种方式接受用户输入时,请确保采取适当的安全预防措施。

答案 1 :(得分:3)

您可能还需要qr运算符quotes its STRING as a regular expression

my $rex = qr/(?$mod)$pattern/;
$isValid = <STDIN> =~ $rex;

答案 2 :(得分:2)

删除$caseSensitive参数,因为在许多情况下它将毫无用处。相反,该函数的用户可以直接在$validationRe正则表达式中编码必要的信息。

当你创建一个像qr/foo/这样的正则表达式对象时,那个模式就会被编译成正则表达式引擎的指令。如果你对正则表达式对象进行字符串化,你将得到一个字符串,当插入到正则表达式时,它将与原始正则表达式对象具有完全相同的行为。最重要的是,这意味着正则表达式对象文字中提供或省略的所有标志都将被保留,并且不能被覆盖!这是设计的,因此无论使用何种上下文,正则表达式对象都将继续表现相同。

这有点干,所以让我们举一个例子。这是一个match函数,它尝试将几个类似的正则表达式应用于字符串列表。哪一个会匹配?

use strict;
use warnings;
use feature 'say';

# This sub takes a string to match on, a regex, and a case insensitive marker.
# The regex will be recompiled to anchor at the start and end of the string.
sub match {
    my ($str, $re, $i) = @_;
    return $str =~ /\A$re\z/i if $i;
    return $str =~ /\A$re\z/;
}

my @words = qw/foo FOO foO/;
my $real_regex = qr/foo/;
my $fake_regex = 'foo';

for my $re ($fake_regex, $real_regex) {
    for my $i (0, 1) {
        for my $word (@words) {
            my $match = 0+ match($word, $re, $i);
            my $output = qq("$word" =~ /$re/);
            $output .= "i" if $i;
            say "$output\t-->" . uc($match ? "match" : "fail");
        }
    }
}

输出:

"foo" =~ /foo/  -->MATCH
"FOO" =~ /foo/  -->FAIL
"foO" =~ /foo/  -->FAIL
"foo" =~ /foo/i -->MATCH
"FOO" =~ /foo/i -->MATCH
"foO" =~ /foo/i -->MATCH
"foo" =~ /(?^:foo)/     -->MATCH
"FOO" =~ /(?^:foo)/     -->FAIL
"foO" =~ /(?^:foo)/     -->FAIL
"foo" =~ /(?^:foo)/i    -->MATCH
"FOO" =~ /(?^:foo)/i    -->FAIL
"foO" =~ /(?^:foo)/i    -->FAIL

首先,我们应该注意到正则表达式对象的字符串表示具有这种奇怪的(?^:...)形式。在非捕获组(?: ... )中,可以在问号和冒号之间添加或删除组内模式的修饰符,而^表示默认的标记集。

现在,当我们查看假的正则表达式时,实际上只是一个被插值的字符串,我们可以看到/i标志的添加会产生预期的差异。但是当我们使用真正的正则表达式对象时,它不会改变任何东西:外部/i不能覆盖(?^: ... )标志。

最好假设所有正则表达式都是正则表达式对象,不应受到干扰。如果从文件加载正则表达式模式,则应该要求正则表达式使用(?: ... )语法来应用flages(例如(?^i:foo)作为qr/foo/i的等效项)。例如。从文件句柄中每行加载一个正则表达式可能如下所示:

my @regexes;
while (<$fh>) {
    chomp;
    push @regexes, qr/$_/;  # will die here on regex syntax errors
}

答案 3 :(得分:0)

您需要使用eval功能。下面的代码将起作用:

sub ask {
    my ($prompt, $validationRe, $caseSensitive) = @_;
    my $modifier = ($caseSensitive) ? "" : "i";
    my $ans;
    my $isValid;

    do {
        print $prompt;
        $ans = <>;
        chomp($ans);

        # What I want to do that doesn't work:
        # $isValid = $ans =~ /$validationRe/$modifier;

        $isValid = eval "$ans =~ /$validationRe/$modifier";

        # What I have to do:
        #$isValid = ($caseSensitive) ?
        #    ($ans =~ /$validationRe/) :
        #    ($ans =~ /$validationRe/i);

    } while (!$isValid);

    return $ans;
}