以下是一个正则表达式,它匹配以逗号分隔的数字列表,并且仅允许输入唯一的数字:
^(?!.*\b(\d+)\b.*\b\1\b)(\d)(,(\d))*$
例如,它允许 1,2,3,但不允许 1,1 或 2,1,1。
有人可以用简单的语言解释一下它是如何工作的吗?
令我困惑的是否定的前瞻断言。在网络上可用的解释中,它显示的语法如下
语法为:X(?!Y),意思是“搜索X,但前提是后面不跟Y”。
参考:https://javascript.info/regexp-lookahead-lookbehind#negative-lookahead
但在我显示的正则表达式中,它不遵循上述语法。那么它是如何工作的呢?
当匹配1,2,3时会发生什么匹配过程?
当不匹配2,1,1时,会发生什么匹配过程?
当 1,2,3 或 2,1,1 匹配时,它们首先匹配正则表达式
(\d)(,(\d))*
的部分,然后针对负向前看部分 (?!.*\b(\d+)\b.*\b\1\b)
断言其匹配结果,或者首先运行负向前看部分然后剩下的部分?
此外,如果我在负向先行部分的
.*
之前和之后删除 \b
,那么它也会开始匹配 1,1 或 2,1,1。那么负向前看部分中删除的.*
有什么意义呢?
注意:我想在 Ruby 代码中使用正则表达式,以防需要通知。
正则表达式的源参考是
https://stackoverflow.com/a/45946721/936494
https://stackoverflow.com/a/45944821/936494
谢谢。
消极前瞻
(?!.*\b(\d+)\b.*\b\1\b)
在字符串开头锚点 (
^
) 之后断言,“情况并非如此 ((?!...)
),在跳过零个或多个字符(行终止符除外)(.*
) 后,存在由一个或多个数字 (\d+
) 组成的字符串,保存到捕获组 1 ((\d+)
),前面和后面是单词边界 (\b
),后面是一个或多个字符 (行终止符除外)(.*
),后跟捕获组 1 (\1
) 的内容,前后是字边界”。
因此,对于字符串
'a 12, *34 *12'
,负向先行失败(因为 '12'
前后有单词边界,重复),而对于字符串 'a 12, *34 512'
则成功(尽管 '12'
重复,但第二个'12'
前面没有单词边界)。如果先行失败,则不会有匹配项,因此不会评估正则表达式的其余部分。如果成功,正则表达式引擎将继续评估正则表达式的其余部分。
由于正则表达式以字符串开头锚点 (
^
) 开头,如果满足前瞻,则不会将正则表达式的字符串指针从字符串的开头移动,在这种情况下,正则表达式实际上变为
^(\d)(,(\d))*$
这满足,例如,
'1,2,3'
。 '1'
将被保存到捕获组 2(捕获组 1 用于前瞻),',2'
将被保存到捕获组 3,'2'
将被保存到捕获组 4,',3'
将被保存到捕获组 3(覆盖 ',2'
)和 '3'
将保存到捕获组 4(覆盖 '2'
)。 演示
鉴于正则表达式中紧随负前瞻的部分,我们发现负前瞻可以按如下方式收紧1:
^(?!(?:\d,)*(\d),(?:\d,)*\1(?=,|\z))(\d)(,(\d))*$
1。我在链接处使用了正向前瞻
(?=,|$)
(而不是 (?=,|\z)
),以便针对多个字符串测试正则表达式。