以任意顺序匹配子模式


Match subpatterns in any order

我有很长的正则表达式,里面有两个复杂的子模式。我如何以任何顺序匹配该子模式?

简化示例:

/(apple)?'s?(banana)?'s?(orange)?'s?(kiwi)?/

我想匹配两者

apple banana orange kiwi
apple orange banana kiwi

这是一个非常简单的例子。就我而言,bananaorange是长而复杂的子模式,我不想做类似的事情

/(apple)?'s?((banana)?'s?(orange)?|(orange)?'s?(banana)?)'s?(kiwi)?/

是否可以对字符类中的字符等子模式进行分组?

根据要求提供 UPD 真实数据:

14:24 26,37 Mb
108.53 01:19:02 06.07
24.39 19:39
46:00

我的字符串更长,但它是重要的部分。在这里你可以看到我需要匹配的两行。First 有两个值:length(14 分 24 秒)和 size 26.37 Mb。第二个有三个值,但顺序不同:size 108.53 Mb,length 01 h 19 m 02 s和date 六月, 07第三个有两个sizelength第四只有length还有几个变体,我需要解析所有值。

有一个非常接近的正则表达式,除了我无法弄清楚如何在不编写两次的情况下以不同的顺序匹配模式。

 (?<size>'d{1,3}'[.,]'d{1,2}'s+(?:Mb)?)?'s?
 (?<length>(?:(?:01:)?'d{1,2}:'d{2}))?'s*
 (?<date>'d{2}'.'d{2}))?

注意:这只是已经分叉良好的大型正则表达式的一部分。

也许在正则表达式"&&"中对"And"的以下改编会有所帮助:

use strict;
use warnings;
while(<DATA>){
    print "Matched: $_" if /^(?=.*'bapple'b)(?=.*'bbanana'b)(?=.*'borange'b)(?=.*'bkiwi'b)/
}
__DATA__
apple banana orange kiwi
apple orange banana kiwi
apple orange banana
kiwiorange bananaapple

输出:

Matched: apple banana orange kiwi
Matched: apple orange banana kiwi

这有效地在正则表达式中创建了一个逻辑 AND,其中合取的顺序并不重要。

另一种编辑方式:

我假设一行不能包含多个日期、长度或大小,我使用一个简单的交替:

$subject = <<<'LOD'
14:24 26,37 Mb
108.53 01:19:02 06.07
24.39 19:39
46:00
LOD;
$pattern = <<<'LOD'
~
^
(?>
    (?> (?<date>   (?> 0[0-9] | 1[012] ) '. (?> [0-2][0-9] | 3[01] )(?! 'h+  Mb) ) 
      | (?<length> (?> (?>01:)? [0-9]{1,2} : [0-9]{2} )              )
      | (?<size>   [1-9][0-9]{0,2} [.,] [0-9]?[1-9] (?> 'h+  Mb)?    )
    )
    (?> 'h | $ )
){1,3} $
~xm
LOD;
preg_match_all($pattern, $subject, $matches, PREG_SET_ORDER);
print_r($matches);

'h:水平白色字符(空格或制表符)

有条件的方式:

您可以使用条件(?(condition)true|false)

(banana)? orange (?(-1)|'g<-1>)

其中-1是捕获组(即:最后一个)的相对位置,'g<-1>是指该捕获组。

您可以通过以下方式转换此条件模式: 如果左侧的第一个捕获组捕获了某些内容,则不执行与捕获组匹配的其他任何操作

PHP 中的示例文本示例:

$subject = <<<'LOD'
apple banana orange kiwi
apple orange banana kiwi
LOD;
$pattern = '~(apple)?'s?(banana)?'s?(orange)?'s?(?(-2)|'g<-2>)'s?(kiwi)?~';
preg_match_all($pattern, $subject, $matches);
print_r($matches);


通知:

您可以使用 oniguruma 语法轻松重用子模式'g<...>

'g<2>    # second capturing group of the pattern
'g<-2>   # second capturing group on the left from the current position
'g<+2>   # the same on the right
'g<size> # refer to the subpattern of the named capture (?<size>...)

如果需要,可以使用定义部分构建模式,例如:

$pattern = <<<'LOD'
~
# definitions 
(?(DEFINE)
    (?<b> banana )
    (?<o> orange )
    (?<fruit> 'g<b> | 'g<o> | kiwi | apple )
)
# pattern
'g<b> 's 'g<o> | 'g<o> 's 'g<b>
~x
LOD;

使用这种工具,您可以避免重复子模式的内容。