在否定先行语法中找到模式时中止正则表达式执行
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
- 无效
- missing delimiter between Username and Password options
Database=somedb;Username=adminPassword=P@ssword!23;Port=1433
- 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,但你的语法更清晰。
在尝试使用正则表达式验证 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
- 无效
- missing delimiter between Username and Password options
Database=somedb;Username=adminPassword=P@ssword!23;Port=1433
- 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,但你的语法更清晰。