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

Recurse subpattern doesn't seem to work with alternation

我想用逗号分隔的数字匹配字符串。简而言之,我最多想匹配 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

在调试器中,当模式递归时,我看到引擎不尝试组中的第二次交替。我希望它能起作用。问题出在哪里?

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

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

在这种情况下,您可以交换备选方案,较长的优先:

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

参见regex demo

一般来说,最佳做法是组中的每个备选方案必须匹配字符串中的不同位置。它们不应在相同位置匹配

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