Perl开关/案例在包含非捕获组的Literal Regex字符串失败'?'

问题描述 投票:4回答:2

我有包含以下行的文本文件:

2/17/2018 400000098627 =2,000.0 $2.0994 $4,387.75
3/7/2018 1)0000006043 2,000.0 $2.0731 $4,332.78
3/26/2018 4 )0000034242 2,000.0 $2.1729 $4,541.36
4/17/2018 2)0000008516 2,000.0 $2.219 $4,637.71

我将它们与/^\s*(\S+)\s+(?:[0-9|\)| ]+)+\s+([0-9|.|,]+)\s+\$/匹配但是我也有一些文件的行以完全不同的格式,我匹配不同的正则表达式。当我打开文件时,我确定哪种格式并在switch / case块中分配$pat = '<regex-string>';

$pat = '/^\s*(\S+)\s+(?:[0-9|\)| ]+)+\s+([0-9|.|,]+)\s+\$/'

但引入非捕获组的?字符用于匹配日期之后和第一个货币金额之前的重复,导致Perl解释器无法编译脚本,报告中止:

syntax error at ./report-dates-amounts line 28, near "}continue "

如果我删除?字符,或用?转义字符替换\?,或首先分配$q = '?'然后在?字符串赋值(即.$q)中用"替换$pat = "/^\s*(\S+)\s+($q:[0-9|\)| ]+)+\s+([0-9|.|,]+)\s+\$/";脚本编译并运行。如果我在switch/case块之外分配正则表达式字符串也可以正常工作。 Perl v5.26.1。

我的代码中也没有任何}continue,正如编译失败中所报告的那样,可能是switch/caseSwitch.pm代码转换为编译器扼杀的本地代码。这是Switch.pm中的某种错误吗?即使我以完全相同的方式使用given/when它也会失败。

#!/usr/local/bin/perl

use Switch;

# Edited for demo
switch($format)
{
    # Format A eg:
    #     2/17/2018 400000098627 =2,000.0 $2.0994 $4,387.75
    #     3/7/2018 1)0000006043 2,000.0 $2.0731 $4,332.78
    #     3/26/2018 4 )0000034242 2,000.0 $2.1729 $4,541.36
    #     4/17/2018 2)0000008516 2,000.0 $2.219 $4,637.71
    #
    case /^(?:april|snow)$/i
    { # This is where the ? character breaks compilation:
        $pat = '^\s*(\S+)\s+(?:[0-9|\)| ]+)+\s+\D?(\S+)\s+\$';

      # WORKS:
      # $pat = '^\s*(\S+)\s+(' .$q. ':[0-9|\)| ]+)+\s+\D' .$q. '(\S+)\s+\$';
    }

    # Format B
    case /^(?:umberto|petro)$/i
    {
        $pat = '^(\S+)\s+.*Think 1\s+(\S+)\s+';
    }
}
regex perl regex-group
2个回答
4
投票

不要使用qazxsw poi。正如@choroba在评论中提到的那样,qazxsw poi使用了一个源过滤器,当你建立时,它会导致神秘且难以调试的错误。

该模块的文档本身说:

一般来说,使用given / when代替。它是在perl 5.10.0中引入的。 Perl 5.10.0于2007年发布。

然而,Switch不一定是一个很好的选择,因为它是实验性的,并且可能在未来发生变化(似乎这个功能是来自Perl v5.28的Switch;所以如果你能避免的话,你绝对不想开始使用它它)。一个很好的选择是使用given/when

almost removed

它可能看起来很奇怪,但是一旦你习惯它,我认为它实际上是合理的。或者,当然,您不能使用这些选项,只需执行以下操作:

for

如果由于某种原因,你仍然想使用for ($format) { if (/^(?:april|snow)$/i) { ... } elsif (/^(?:umberto|petro)$/i) { ... } } :使用sub pattern_from_format { my $format = shift; if ($format =~ /^(?:april|snow)$/i) { return qr/^\s*(\S+)\s+(?:[0-9|\)| ]+)+\s+\D?(\S+)\s+\$/; } elsif ($format =~ /^(?:umberto|petro)$/i) { return qr/^(\S+)\s+.*Think 1\s+(\S+)\s+/; } # Some error handling here maybe } 而不是Switch

我不知道为什么会发生这个错误,但是,m/.../说:

还有,用raw指定的正则表达式的存在?...?分隔符可能会导致神秘的错误。解决方法是使用m?...?代替。

我最初误读,因此试图使用/.../而不是documentation,这解决了这个问题。


2
投票

另一个选择而不是m/../ / /../链将循环遍历哈希,哈希将正则表达式映射到应分配给if的值:

elsif

对于更一般的情况(即,如果你做的不仅仅是将字符串分配给标量),你可以使用相同的通用技术,但使用coderefs作为哈希的值,从而允许它执行基于任意的$pat在比赛中。

这种方法可以涵盖通常与#!/usr/local/bin/perl my %switch = ( '^(?:april|snow)$' => '^\s*(\S+)\s+(?:[0-9|\)| ]+)+\s+\D?(\S+)\s+\$', '^(?:umberto|petro)$' => '^(\S+)\s+.*Think 1\s+(\S+)\s+', ); for my $re (keys %switch) { if ($format =~ /$re/i) { $pat = $switch{$re}; last; } } / sub结构相关的各种功能,但请注意,由于条件是从哈希的键中提取的,因此它们将以随机顺序进行评估。如果您有可以匹配多个条件的数据,则需要采取额外的预防措施来处理该问题,例如使用具有正确顺序的条件的并行数组或使用switch而不是常规哈希。

© www.soinside.com 2019 - 2024. All rights reserved.