子返回匹配的正则表达式组

时间:2020-11-12 13:36:46

标签: regex perl

我正在解析表格的行

12:34 SomeEvent: 0 Lorem ipsum dolor sit amet

我有一个处理程序子程序,该处理程序子程序仅获得一行,并使用given/when根据正则表达式匹配将其传递给更特定的处理程序子程序,例如,上面的行将传递给_someevent

在这些特定的处理程序中,我想提取该行的0部分,就像ID。

为此,我写了以下内容:

sub _getid ($) { $_[0] =~ /\d+:\d+ \w+: (\d+)/ }

以这种方式使用时,该潜艇似乎可以工作:

say _getid("12:34 SomeEvent: 0 Lorem ipsum dolor sit amet\n");

但是当我将结果分配给变量时:

my $id = _getid("12:34 SomeEvent: 0 Lorem ipsum dolor sit amet\n");
say "ID = $id";

它总是变成1。我认为这与=~正则表达式匹配实际上返回一个列表或某物并将我将其分配给标量这一事实有关?

我提出了以下建议:

sub _getid ($) {
    $_[0] =~ /\d+:\d+ \w+: (\d+)/;
    $1; # or return $1;
}

但是必须有一种更好,更优雅的方法来解决问题。

4 个答案:

答案 0 :(得分:3)

您被上下文所伤。从perlop(特别是Regexp Quote-Like Operators的部分)开始:

/ PATTERN / msixpodualngc

在字符串中搜索模式匹配,并在标量上下文中返回 如果成功,则为true;如果失败,则为false。

后来:

匹配列表上下文

如果不使用/ g选项,则列表上下文中的m //返回一个列表 由与括号中的括号匹配的子表达式组成 模式,即($ 1,$ 2,$ 3 ...)(请注意,这里的$ 1等 组)。当模式中没有括号时,返回值 是成功的清单(1)。带或不带括号的空格 失败时返回列表。

转到您的代码。

say _getid("12:34 SomeEvent: 0 Lorem ipsum dolor sit amet\n");

say()在其参数上强加了列表上下文,因此您可以获得捕获的列表。您只有一个捕获,因此列表中只有一个元素(您的ID),这就是要打印的内容。

my $id = _getid("12:34 SomeEvent: 0 Lorem ipsum dolor sit amet\n");

分配比例变量是标量上下文的一个很明显的例子。因此,您将获得文档第一部分摘录中描述的行为。您看到的“ 1”是真实值。

[更新:我对问题的解释(高于此点的所有内容)很好。但是我建议的修复方法(低于此点的东西)并没有我最初想象的有用。 TLP和ikegami的其他答案都包括更好的解决方案。]

要解决此问题,您需要在子例程调用中添加列表上下文。最简单的方法是用列表赋值替换标量赋值-通过在变量周围加上括号。

my ($id) = _getid("12:34 SomeEvent: 0 Lorem ipsum dolor sit amet\n");

答案 1 :(得分:2)

确保子例程始终返回标量的一种优雅(?)方法是在正则表达式匹配返回的列表上使用下标:

sub _getid {
    ($_[0] =~ /\d+:\d+ \w+: (\d+)/)[0];    # subscript makes parenthesis return
                                           # 1st element of list
}

当然,这都是非常“ golfy”的代码。我可能会更明确地编写此子例程,以使该代码对于其他人而言实际上是可读的:

sub _getid {
    my $str = shift;
    my ($return) = $str =~ /\d+:\d+ \w+ (\d+)/;
    return $return;
}

关于代码的一些注释。

  • 请注意,当您使用$_[0]时,您可能会无意间更改参数,因为您是直接访问它的。较安全的选择是将内容复制到新的词法范围变量,就像上面的示例一样。

请考虑例如sub foo { $_[0]++ }。如果运行my $foo = 0; foo($foo); print $foo;,则将打印1,显示$foo已被子例程更改。如果尝试使用foo(2),您还会收到相当奇怪的错误Modification of a read-only value attempted

  • 您可能不应该将原型用于子例程。它们在Perl中有特殊用途,这不是大多数人所想的。即您应该执行sub foo { ... }而不是sub foo ($) { ... }。文档here

答案 2 :(得分:2)

你有这个:

sub _getid ($) {
    $_[0] =~ /\d+:\d+ \w+ (\d+)/;
    $1; # or return $1;
}

如果字符串不匹配(返回一些“随机”字符串),上述操作将失败。以下内容也可以使用,但是更安全一些:

# Match in scalar context returns whether the match succeeded or not.
# Returns $1, or undef if no match.
sub _getid { $_[0] =~ /\d+:\d+ \w+ (\d+)/ ? $1 : undef }
# Match in list context returns captures.
# Using a slice, this returns $1, or undef if no match.
sub _getid { ( $_[0] =~ /\d+:\d+ \w+ (\d+)/ )[0] }

答案 3 :(得分:-2)

代码按其设计的方式工作,而不是OP期望的方式。

第一个错误隐藏在匹配模式中,因为在 SomeEvent 之后它没有解决:

标量上下文中的匹配结果将指示是否存在匹配-将其视为布尔变量。

如果使用修饰符/g,并且字符串中发生多次匹配,则匹配结果将为匹配计数。

如果OP的左侧有一个列表(数组)变量,则他将匹配的组填充到数组中,但是原始代码未使用此方法。

OP的作用在_getid()子例程的修改版本中得到了证明。

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

my $str = "12:34 SomeEvent: 0 Lorem ipsum dolor sit amet\n";
my $var;

$var = $str =~ /\d+:\d+ \w+ (\d+)/;
say "-[$var]-";

$var = $str =~ /\d+:\d+ \w+: (\d+)/;
say "-[$var]-";

my $id = _getid($str);
say '_getid returned: ' . $id;

sub _getid {
    my $str = shift;
    
    return $1 if $str =~ /\d+:\d+ \w+: (\d+)/;
    
    return undef;
}

输出

-[]-
-[1]-
_getid returned: 0

文档:perlre

相关问题