在否定先行语法中找到模式时中止正则表达式执行

Abort regex execution when pattern found in negative lookahead syntax

在尝试使用正则表达式验证 SQL 服务器的连接字符串模式时,我取得了以下结果:

^(?!.*?(?<=^|\;)[a-zA-Z]+( [a-zA-Z]+)*(\=[^\;]+?\=[^\;]*)?(\;|$))+([a-zA-Z]+( [a-zA-Z]+)*\=[^\;]+\;?)+$

使用的示例字符串是:

option=value;missingvalue;multiple assignment=123=456

* (hosted and tested in regex101)

而且,正如预期的那样,字符串不匹配。问题是我认为这可能不是标准的、推荐的也不是最佳的正则表达式实现——尤其是在否定前瞻部分,考虑到即使在成功匹配之后它也会遍历整个字符串。

我将尝试在下面分解它的工作原理:


负前瞻

1. ^(?!.*?(?<=^|;)

负先行模式从字符串的开头开始或在分号字符之后递归遍历

2。 [a-zA-Z]+( [a-zA-Z]+)*(=[^;]+?=[^;]*)?(;|$))+

匹配简单的或复合的选项名称——也就是说,只是 [a-zA-Z]+ (强制性的)或者另外 ( [a-zA-Z]+)* 任意次数;之后,当任何给定选项有多个连续赋值时,有一个可选组会尝试匹配;最后它以 ;$ (字符串结尾)结束——如果是第一个,先行模式从头开始(递归)

常规模式匹配

([a-zA-Z]+([a-zA-Z]+)*=[^;]+;?)+$

这里没有什么新的东西要说,除了这是在初始 Negative Lookahead 彻底 scan/validation.

之后实际匹配字符串的模式

我不能否认它有点符合我的预期,但我忍不住觉得我对正则表达式的工作有一些误解。

有没有一种更简单的方法可以做到这一点,同时避免多次使用上述模式递归地向前看?

编辑: 根据要求,一些更接近现实生活的例子如下——对于有效和无效的格式:

Database=somedb;Username=admin;Password=P@ssword!23;Port=1433
  1. missing delimiter between Username and Password options
Database=somedb;Username=adminPassword=P@ssword!23;Port=1433
  1. missing value for Port option
Database=somedb;Port;Username=admin;Password=P@ssword!23

以下字符串仅接受字母作为名称。出于测试目的,它接受值中除等号和分号之外的任何字符。这需要定义为需要排除行结束符和制表符等字符。 我们有一个否定的前瞻来禁止在值中使用第二个等号,并且有一个否定的回顾来禁止在结束之前使用 semi-colon。请注意,您的“正确”示例被发现是错误的,因为末尾没有 semi-colon 如果我们尝试阻止另一轮,则无法匹配正则表达式。
我在名称中添加了一个可选的单个 space 以匹配“连接超时”和类似的

/^(\s*[a-zA-Z]+ ?[a-zA-Z]+=[^=;]+;)+$/gm

我也允许名字前面有 spaces。
我们的字符串由 ^行首 (开始组
\s*可选白色space前名字
[a-zA-Z]+ ?[a-zA-Z]+名称在可选 space 前后至少包含一个字母。这意味着至少有两个字母
=一个等号
(开始内群
(?!\=) 等号的否定前瞻
[^=;] 除等号和 semi-colon 之外的任何字符至少一次
; 文字 semi-colon.
){4,}关闭外组至少重复4次
$ 行尾

感谢 Casimir et Hippolyte 的改进。我在问题之后使用 look-aheads 和 look-backs,但你的语法更清晰。