1

如何摆脱使用ifPerl 正则表达式的构造中未初始化的值的使用?

使用下面的代码时,我会使用未初始化的值消息。

     if($arrayOld[$i] =~ /-(.*)/ || $arrayOld[$i] =~ /\#(.*)/)

使用下面的代码时,我没有得到任何输出。

     if(defined($arrayOld[$i]) =~ /-(.*)/ || defined($arrayOld[$i]) =~ /\#(.*)/)

给定上面的代码,检查变量是否具有值的正确方法是什么?

4

3 回答 3

2

尝试:

if($arrayOld[$i] && $arrayOld[$i] =~ /-|\#(.*)/)

这首先检查 $arrayOld[$i] 的值,然后再针对它运行正则表达式。(也将 合并||到正则表达式中。)

于 2012-05-26T01:15:21.130 回答
1

从您评论中的错误消息中,您正在访问@arrayOld未定义的元素。在没有看到其余代码的情况下,这可能表明您的程序中存在错误,或者它可能只是预期的行为。

如果您了解为什么 $arrayOld[$i]undef,并且您希望在不收到警告的情况下允许这样做,那么您可以做几件事。Perl 5.10.0 引入了定义或运算符//,您可以使用它来替换空字符串undef

use 5.010;
...
if(($arrayOld[$i] // '') =~ /-(.*)/ || ($arrayOld[$i] // '') =~ /\#(.*)/)

或者,您可以关闭警告:

if (do { no warnings 'uninitalized'; 
         $arrayOld[$i] =~ /-(.*)/ || $arrayOld[$i] =~ /\#(.*)/ })

在这里,我使用do来限制禁用警告的时间。但是,关闭警告也会抑制如果 was 收到的$i警告undef。Using//允许您准确指定允许的undef值,以及应该使用什么值而不是undef.

注意:正在对定义defined($arrayOld[$i]) =~ /-(.*)/函数的结果运行模式匹配,这将是一个真/假值;不是您要测试的字符串。

于 2012-05-26T01:19:28.293 回答
1

要狭隘地回答您的问题,您可以在该行代码中防止未定义值警告

if (defined $i && defined $arrayOld[$i]
      && ($arrayOld[$i] =~ /-(.*)/ || $arrayOld[$i] =~ /\#(.*)/))
{
  ...;
}

也就是说,计算其中一个$i或表达式$arrayOld[$i]可能会导致未定义的值。注意上面写的额外的括号层,因为 和 之间的优先级不同&&||前者绑定更紧密。对于您问题中的特定模式,您可以通过将您的模式组合成一个正则表达式来回避这个优先级问题,但在一般情况下这可能会很棘手。

我建议不要使用上面令人不快的代码。继续阅读以查看一个优雅的问题解决方案,它让 Perl 为您完成工作并且更容易阅读。

回头看

您之前的问题的更广泛的背景来看,$i是一个循环变量,并且肯定会通过构造来定义,因此测试$i是多余的。您的代码盲目地从 中提取元素@arrayOld,Perl 很乐意接受。在什么都没有的情况下,您会得到未定义的值。

这种一对一的偷看和戳在 C 程序中很常见,但在 Perl 中,它几乎总是一个危险信号,您可以更优雅地表达您的算法。考虑下面完整的工作示例。

工作演示

#! /usr/bin/env perl

use strict;
use warnings;
use 5.10.0;  # given/when

*FILEREAD = *DATA;  # for demo only

my @interesting_line = (qr/-(.*)/, qr/\#(.*)/);

$/ = ""; # paragraph mode
while(<FILEREAD>) {
  chomp;
  my @arrayOld = split /\n/;
  my @arrayNewLines;

  for (1 .. @arrayOld) {
    given (shift @arrayOld) {
      push @arrayNewLines, $_ when @interesting_line;
      push @arrayOld, $_;
    }
  }

  print "\@arrayOld:\n",      map("$_\n", @arrayOld), "\n",
        "\@arrayNewLines:\n", map("$_\n", @arrayNewLines);
}

__DATA__
#SCSI_test         # put this line into  @arrayNewLines      
kdkdkdkdkdkdkdkd
dkdkdkdkdkdkdkdkd
- ccccccccccccccc  # put this line into @arrayNewLines

前台事项

线

use 5.10.0;

启用 Perl 的given/ whenswitch 语句,这是一种很好的方式来决定哪个数组获得给定的输入行。

正如评论所示

*FILEREAD = *DATA;  # for demo only

是为了这个 Stack Overflow 演示的目的。在您的真实代码中,您有open FILEREAD, .... 将您问题的输入放入 Perl 的DATA文件句柄中,可以在一个独立的单元中显示代码和输入,然后我们使用别名FILEREADDATA以便其余代码可以毫不费力地放入您的代码中。

主要事件

处理的核心是

for (1 .. @arrayOld) {
  given (shift @arrayOld) {
    push @arrayNewLines, $_ when @interesting_line;
    push @arrayOld, $_;
  }
}

请注意,没有defined检查,甚至没有明确的正则表达式匹配!没有$i$arrayOld[$i]!这是怎么回事?

您从@arrayOld包含当前段落中的所有行开始,并希望以有趣的行结束,@arrayNewLines而其他所有内容都留在@arrayOld. @arrayOld上面的代码从with中取出下一行shift。如果该行很有趣,我们push将它放到@arrayNewLines. 否则,我们将其放回@arrayOld.

语句修饰符when @interesting_line与来自 的主题执行隐式智能匹配given。正如“详细智能匹配”中所解释的,当对数组进行智能匹配时,Perl 会隐式循环它并在第一次匹配时停止。在这种情况下,数组@interesting_line包含已编译的正则表达式,这些正则表达式匹配您要移动到的行@arrayNewLines。如果当前行($_感谢given)与这些模式中的任何一个都不匹配,则返回@arrayOld.

我们将前面的过程精确地执行scalar @arrayOld多次,即当前段落中的每一行执行一次。这样,我们只处理一次所有内容,而不必担心当前数组索引在哪里的繁琐记账。@arrayOld在这么多 s 之后剩下的任何东西都shift必须是我们push重新编入它的行,这些行是按输入中出现的顺序排列的无趣的行。

样本输出

对于您问题中的输入,输出是

@arrayOld:
kdkdkdkdkdkdkdkd
dkdkdkdkdkdkdkdkdkd

@arrayNewLines:
#SCSI_test # 将此行放入@arrayNewLines      
- ccccccccccccccc # 将此行放入@arrayNewLines
于 2012-05-26T12:41:20.583 回答