递归子模式似乎不适用于交替

时间:2019-08-02 08:25:00

标签: regex pcre

我想用数字用逗号分隔字符串。简而言之,我想最多匹配1-16范围内的8个数字。因此该字符串1,2,3,4,5,6,7,8可以,而1,2,3,4,5,6,7,8,9则不能,因为它有9个数字。 16也可以,但是17不好,因为17不在范围内。

我尝试使用此正则表达式^(?:(?:[1-9]|1[0-6]),){0,7}(?:[1-9]|1[0-6])$ 而且效果很好。我使用交替来匹配1​​-16之间的数字,然后使用0..7重复,最后以逗号结尾,然后使用相同的结尾(不带逗号)。但是我不喜欢子模式的重复,因此我尝试(?1)来递归第一个捕获组。我的正则表达式看起来像^(?:([1-9]|1[0-6]),){0,7}(?1)$。但是,当最后一个数字有两个字母(10-16)时,这不会产生匹配。它确实匹配1,1,但不匹配1,10。我不懂为什么。

我创建了一个问题示例。

https://regex101.com/r/VkuPqP/1

在调试器中,当模式递归时,我发现引擎不尝试从组中进行第二次轮换。我希望它能工作。问题出在哪里?

1 个答案:

答案 0 :(得分:1)

发生这种情况是因为PCRE中的regex subroutines是原子的。

您拥有的正则表达式可以重写为^(?:([1-9]|1[0-6]),){0,7}(?>[1-9]|1[0-6])$,请参见its demo(?>...|...)不允许回溯到该​​组,因此,如果第一个分支“获胜”(如您的示例所示),则在下一个子模式失败时将不尝试后续分支(此处$失败)匹配1后匹配字符串的末尾-后跟0)。

在这种情况下,您可以交换其他选择,时间越长越优先:

^(?:(1[0-6]|[1-9]),){0,7}(?1)$

请参见regex demo

通常,最佳做法是组中的每个替代项都必须在字符串内的不同位置匹配。它们不应在相同位置匹配

如果您不能重写替代组,以使每个替代组都匹配字符串中的唯一位置,则应重复使用该组,而无需使用正则表达式子例程。