使用命名子模式的正则表达式看不到最佳匹配

时间:2015-12-17 15:29:09

标签: regex pcre

在reg exp中使用已定义的子模式时,它不会选择最佳匹配,但会在第一次匹配时停止。我忘记了一些旗帜吗?

正则表达式:(?<minutes>[0-9]|[1-5][0-9]):(?&minutes); 测试字符串:47:24;

表达式不匹配:

pic 1 (47:24;)

但字符串47:2;已正确匹配:

pic 2 (47:2;)

如果我改变了或者#39;条件为[1-5][0-9]|[0-9],reg exp (?<minutes>[1-5][0-9]|[0-9]):(?&minutes);效果很好。还有其他方法可以制作字符串&#39; 47:24;&#39;匹配而不反转&#39;或&#39;条件?

2 个答案:

答案 0 :(得分:3)

模式从左到右匹配,替代品也从左到右尝试。这就是NFA正则表达式引擎的工作方式。 PCRE还有一个DFA引擎,它会尝试找到最长的匹配,但它不会暴露给PHP。

因此,如果您的模式类似a|bba的子集,则引擎会首先尝试a并成功。 <{1}}部分将从不匹配。

你可以写b,但这似乎是多余的。

只需使用\b(?:[1-5][0-9]|[0-9])\b(如stribizhev建议的那样)一直使用它。 \b[1-5]?[0-9]\b是一个单词边界,它会确保您匹配整数,而不是jsut更大数字的几个数字。

答案 1 :(得分:2)

使用PCRE,递归组是原子的(请参阅此article)。这就是正则表达式引擎无法在(?&minutes)中回溯的原因。

42:24;中,2的{​​{1}}与第一个分支24匹配(自第一次获胜以来),但是当模式失败时,因为有{ {1}}在字符串中而不是[0-9],正则表达式引擎无法在4子模式内回溯以测试第二个分支;(您可以查看debugger

解决方案:不要对如此小的子模式使用递归,它没用,也毫无意义(特别是如果你使用捕获组的名称)。写点如下:

(?&minutes)

或为什么不:

[1-5][0-9]

似乎是多余的,但是如果你想提取分钟和秒钟(或者根本不使用组),它是有意义的并且很有用。毕竟,如果你使用命名捕获,你的目标不是写出世界上最短的模式。

如果你无法避免改变:

  • 您可以按照卢卡斯的建议放置最长的分支:(?<minutes>[1-5]?[0-9]):(?<seconds>[1-5]?[0-9]);
  • 您还可以使用互斥分支:(?(DEFINE)(?<sex>[1-5]?[0-9]) for "sexagesimal", not for what you think) (?<minutes>(?&sex)):(?<seconds>(?&sex)); [1-5][0-9]|[0-9] (在这种情况下订单无关紧要)

请注意,递归组的这种行为特别适用于PCRE,它与Perl或Ruby不同。