【问题标题】:Perl: Unexpected match variables when using ?: operatorPerl:使用 ?: 运算符时出现意外的匹配变量
【发布时间】:2011-09-06 03:37:51
【问题描述】:

尝试在 Perl 中清除一些变量,以下代码效果很好:

if ($year =~ /^(\d{4})$/) {
        $year = $1;
} else {
        &invalid("year");
}

在上面的例子中,$1 包含 $year(如果有效)。但是,当使用?: 运算符时,$1 在有效时包含“1”:

($year =~ /^(\d{4})$/) ? $year = $1 : &invalid("year");

有人知道我哪里有错吗?我很困惑为什么会这样。它只发生在这台机器上。或者更确切地说,我已经成功使用了 ?运算符用于返回多年的正确匹配变量。我还没有在任何其他机器上尝试过这段代码。

This is Perl, v5.8.8 built for x86_64-linux-thread-multi

【问题讨论】:

  • 您确定这是您正在运行的代码吗?您能否包含一个小型的独立脚本来演示您的问题?
  • 由于优先级问题,条件运算符内的赋值通常不正确。 (例如c ? x=1 : x=2 表示(c ? x=1 : x) = 2)您提供的代码没有您描述的问题或任何优先级问题。
  • OT:不要像&invalid(...)那样用&调用子程序;它不符合您的预期...使用invalid(...)
  • @pavel,或者更准确地说,它比大多数人预期的要多。 (它确实调用了 sub,这是预期的行为。)
  • @jonathan:它禁用原型检查,更重要的是,当不带参数使用时,子例程将使用@_ 数组,就像调用例程时一样...

标签: regex perl cgi taint-checking


【解决方案1】:

注意这里的优先级。

对比http://codepad.org/vEPnhWfH 可以按预期工作,而http://codepad.org/nXVU5CA7 则不能。当然,我稍微调整了代码以避免调用invalid,但我怀疑这可能是问题的根源。

在这两种情况下,尽管$1 包含“2011”,所以也许您应该按照 mu 第一条评论中的要求显示其他代码。

更新

我将键盘示例更改为使用对 &invalid 的调用,但错误未出现。

sub invalid {
    print("INVALID\n");
}

sub check_with_if {
    print("Trying with if-statement\n");
    my $year = shift;
    if ($year =~ /^(\d{4})$/) {
        $year = $1;
    } else {
        &invalid($year);
    }
    print("\$1 is $1 and \$year is $year"."\n");
}

sub check_with_conditional {
    print("Trying with conditional expression\n");
    my $year = shift;
    ($year =~ /^(\d{4})$/) ? $year = $1 : &invalid($year);
    print("\$1 is $1 and \$year is $year"."\n");
}

check_with_if("2011");
check_with_conditional("2011");

输出是

Trying with if-statement 
$1 is 2011 and $year is 2011
Trying with conditional expression
$1 is 2011 and $year is 2011

http://codepad.org/z22GMEcn

EDIT 2 也适用于 Mac 上的 5.12.3。

我同意 Jonathan 的观点,您可能在 5.8.8 中发现了一个错误,此后该错误已得到修复。如果您非常好奇,您可以按照自己的方式完成 Perl 更改,例如:

【讨论】:

  • 有趣:在第二个例子中,你有($year =~ /^(\d{4})$/) ? $year = $1 : $year = "invalid"; 并产生invalid。将其更改为($year =~ /^(\d{4})$/) ? $year = $1 : &invalid("year"); 并且不调用子invalid()。 (测试:MacOS X 10.7.1 上的 Perl 5.14.1)。我不确定我是否有解释。
  • @Jonathan 我想我在第一次更新中确实做到了。原因是优先级:赋值的优先级低于条件。使用错误数据调用无效子程序:codepad.org/RUzgLQDa(除非我不理解您的评论。)
  • 一直发生 :) 想知道 OP 是否会添加到问题中?
【解决方案2】:

Damien Conway 的 Perl Best Practices 解释了使用匹配变量 $1 等的一些陷阱,其中之一是:

  • 如果正则表达式不匹配任何内容,$1 不会变为 undef,但会保留其先前的值(当然,在大多数情况下是 undef)

我的直觉告诉我你的上下文有问题 - 你的表达式可能在标量上下文中进行评估,因此在它有效时返回匹配的数量。

所以,我会尝试重写代码:

($year) = ( $year =~ /^(\d{4})$/) or &invalid("year");

【讨论】:

  • 如果年份无效,则会产生更改 $year 的副作用。这将防止该值在错误消息中使用。
  • 确实有我的($validated_year) = ( $year =~ /^(\d{4})$/) or &invalid("year");可以避免的副作用
【解决方案3】:

使用 Perl 5.14.1(在 MacOS X 10.7.1 上),此脚本似乎可以正常工作:

use strict;
use warnings;

my $year = "1984";

sub invalid
{
    my($year) = @_;
    print "Invalid year $year\n";
}

if ($year =~ /^(\d{4})$/)
{
    $year = $1;
}
else
{
    &invalid("year");
}


print "Year1: $year\n";

$year = "1984";
($year =~ /^(\d{4})$/) ? $year = $1 : &invalid("year");

print "Year2: $year\n";

它产生:

Year1: 1984
Year2: 1984

如果相同的脚本在 Perl 5.8.8 中产生了不同的答案,那么可能你遇到了一个已修复的错误并且升级是有序的。如果 Perl 5.8.8 产生相同的答案(与 Perl 5.14.1),那么您还没有描述您的问题,以便我可以理解它并重现问题。

【讨论】:

  • 我在使用系统提供的 Perl 5.8.8 的 RHEL 5 x86_64 机器上尝试了上面的脚本,并在 MacOS X 上得到了与 5.14.1 相同的输出。我还尝试了我的自制Perl 5.12.1 在 RHEL 5 上得到了相同的结果。
猜你喜欢
  • 1970-01-01
  • 2017-02-07
  • 2011-06-10
  • 1970-01-01
  • 2014-08-06
  • 2022-01-05
  • 2021-10-14
  • 2014-08-31
  • 2020-12-18
相关资源
最近更新 更多