【问题标题】:Multiple patterns with "|" ("or"): determine all matches?带有“|”的多个模式(“或”):确定所有匹配项?
【发布时间】:2016-04-23 17:09:23
【问题描述】:

我正在解析日志文件,并希望一次将一行与多个模式匹配。我知道我可以从Regexp::Assemble 中受益,正如this question 的答案中所建议的那样。不幸的是我不能使用它(因为它需要部署在数十台运行 Perl 从 5.8.8 到 5.18.0 以及 AIX 和 SLES11/12 的生产机器上)。

所以我想我必须坚持使用|。我尝试了以下代码:

#!/usr/bin/env perl
use strict;
use warnings;
use Data::Printer;
my @matches;

@matches = ( 'sjd one skj two sjf' =~ /(\bone\b)|(\btwo\b)|(\bthree\b)/ );
p @matches;

给予

[
    [0] "one",
    [1] undef,
    [2] undef
]
@matches = ( 'sjd onnne skj two sjf' =~ /(\bo.+e\b)|(\btwo\b)|(\bthree\b)/ );
p @matches;

给予

[
    [0] "onnne",
    [1] undef,
    [2] undef
]
@matches = ( 'sjd ONE skj two sjf' =~ /(\bone\b)|(\btwo\b)|(\bthree\b)/ );
p @matches;

给予

[
    [0] undef,
    [1] "two",
    [2] undef
]
@matches = ( 'sjd ONE skj TWO sjf' =~ /(\bone\b)|(\btwo\b)|(\bthree\b)/ );
p @matches;

给予

[]

由此我可以判断是否有匹配。 @matches 不匹配或包含对应位置的匹配是假的。 但在前两种情况下,实际上有 two 个匹配项。有没有办法让代码输出在第一种情况下都匹配,比如:

@matches = ( 'sjd one skj two sjf' =~ /(\bone\b)|(\btwo\b)|(\bthree\b)/ );
p @matches;
[
    [0] "one",
    [1] "two",
    [2] undef
]

即这样@matches 包含 all 匹配项,而不仅仅是第一个匹配项? 或者我是否必须使用 if/else 级联或其他方式遍历我的模式?

更新

重新阅读我的问题和您的 cmets,我意识到我的问题并不清楚。很抱歉。
实际上我确实关心匹配的模式以及匹配的结果,即对于线

$line = 'xxx two xxx one xxx';

和模式/one//t.o//three/ 我想知道模式/one/ 匹配"one" 和模式/t.o/ 匹配"two"。最好是一步完成。

【问题讨论】:

  • 您不需要 5.10 即可使用 Regexp::Assemble。事实上,Regexp::Assemble 的主要用途和您感兴趣的(优化交替)在 5.10 中变得无用(因为它成为内置的)。
  • @ikegami 您指的是~~ 运算符,对吗?不幸的是,我的脚本还必须在安装了 no 附加功能的 AIX 下与 5.8.8 一起运行。 :-(
  • 不,~~ 是实验性的,可能会改变。它不应该被使用。 // R::A 基本上是 my $re = join '|', @regex_patterns;,但是以 5.10+ 中不需要的方式优化输出 // 这不是真的。您安装了一个脚本。因此,您可以安装 R::A。
  • 我还发现 ~~ 很可疑,很高兴听到 R::A 对我的情况来说似乎是多余的。 // 技术上是正确的,但是一些“来自互联网的代码”必须得到 ITSec 部门人员的确认。没那么容易。

标签: perl pattern-matching


【解决方案1】:

以下查找所有匹配项,但输出不是您要求的:

my @matches = /\b(one|two|three)\b/g;

以下内容完全符合您的要求:

my @matches;
($matches[0]) = /\b(one)\b/;
($matches[1]) = /\b(two)\b/;
($matches[2]) = /\b(three)\b/;

以下内容在一场比赛中完成:

my @matches = /
   ^
   (?= .* /\b(one)\b )
   (?= .* /\b(two)\b )
       .* /\b(three)\b
/xs;

【讨论】:

  • 如果您不在乎是什么匹配触发了捕获,我建议您采用这种最佳方式。例如,如果匹配此模式,我想在日志文件中捕获一行。
  • 更新了我的答案。
  • 我想我必须修改我的问题:实际上我确实关心哪个匹配触发了捕获,因为该行需要进一步处理(取决于哪个匹配)。跨度>
【解决方案2】:

你关心比赛的位置吗?如果不是,那么您应该使用“非捕获括号”:(?:..) 结合全局匹配标志 /g

@matches = ( 'sjd one skj two sjf' =~ /(?:\bone\b)|(?:\btwo\b)|(?:\bthree\b)/g );

这会产生:

$VAR1 = [
          'one',
          'two'
        ];

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-07-15
    • 2017-05-14
    • 1970-01-01
    • 2012-05-18
    • 2015-10-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多