我想用数字用逗号分隔字符串。简而言之,我想最多匹配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
在调试器中,当模式递归时,我发现引擎不尝试从组中进行第二次轮换。我希望它能工作。问题出在哪里?
答案 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。
通常,最佳做法是组中的每个替代项都必须在字符串内的不同位置匹配。它们不应在相同位置匹配。
如果您不能重写替代组,以使每个替代组都匹配字符串中的唯一位置,则应重复使用该组,而无需使用正则表达式子例程。