2

我目前正在通过 REGEX 行匹配表行从文件中的表中提取值。

foreach my $line (split("\n", $file)) {
    if ($line =~ /^(\S+)\s*(\S+)\s*(\S+)$/) {
        my ($val1, $val2, $val3) = ($1, $2, $3);

        # $val's used here
    }
}

为了代码的清晰起见,我特意分配了 val。我的一些表行包含 10+ vals(又名列) - 有没有更有效的方法来分配 vals 而不是... = ($1, $2, ..., $n)?

4

3 回答 3

9

列表上下文中的匹配产生捕获组的列表。如果失败,则返回一个空列表,这是错误的。因此,您可以

if( my ( $val1, $val2, $val3 ) = $line =~ m/^(\S+)\s*(\S+)\s*(\S+)$/ ) {
   ...
}

但是,此代码中明显存在许多危险信号。该正则表达式捕获看起来非常类似于拆分:

if( my ( $val2, $val2, $val3 ) = split ' ', $line ) {
   ...
}

其次,为什么用换行符分割 $file;如果您正在读取文件的内容,更好的是一次实际读取一行:

while( my $line = <$fh> ) {
   ...
}
于 2012-04-18T14:48:56.053 回答
2

我假设这不是您的实际代码,因为如果是这样,它将不起作用:

foreach my $line (split("\n", $file)) {
    if ($line =~ /^(\S+)\s*(\S+)\s*(\S+)$/) {
        my ($val1, $val2, $val3) = ($1, $2, $3);
    }
# all the $valX variables are now out of scope
}

您还应该知道,它\s*也会匹配空字符串,并可能导致细微的错误。例如:

"a bug" =~ /^(\S+)\s*(\S+)\s*(\S+)$/;
# the captures are now: $1 = "a"; $2 = "bu"; $3 = "g"

即使这\S+是贪婪的事实,锚点^ ... $也会强制正则表达式适合,因此允许空字符串拆分单词。

如果您的意图是捕获由空格分隔的所有单词,则使用split是您的最佳选择,正如其他人已经提到的那样。

open my $fh, "<", "file.txt" or die $!;
my @stored;
while (<$fh>) {
    my @vals = split;
    push(@stored, \@vals) if @vals; # ignore empty values
}

这会将任何捕获的值存储到二维数组中。直接使用文件句柄并逐行读取是首选方法,除非出于某种原因您实际上需要将整个文件保存在内存中。

于 2012-04-18T15:16:57.077 回答
1

看起来您只是在使用带有空格分隔符的表。您可以使用split功能:

@valuearray = split(" ", $line)

然后将元素处理为:

@valuearray[0] ,@valuearray[1]ETC..

于 2012-04-18T14:52:28.010 回答