我正在编写一个抽象函数,它会询问用户一个给定的问题并根据给定的正则表达式验证答案。重复该问题,直到答案与验证正则表达式匹配。但是,我还希望客户端能够指定答案是否必须符合大小写。所以像这样:
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:有没有办法动态指定正则表达式的修饰符?
答案 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;
}