如果您有严格的正则表达式(连接、联合、闭包),您将能够将其转换为 POSIX 正则表达式。如果您有 POSIX 正则表达式,您将能够将其转换为任何更复杂的正则表达式引擎。
假设(请先阅读)
此答案假定 JavaScript 正则表达式的支持级别(具有前瞻但没有后瞻),但我们也假定 . 无一例外地匹配任何字符。此处的正则表达式将假定整个字符串与模式匹配。
问题 1
^(?=(?:b*ab*a)*b*$)(?=(?:a*ba*b)*a*ba*$)[ab]*$
可以为这个问题构造一个 DFA,因此,只有连接、联合和闭包的正则表达式存在。 (DFA 中状态的数字表示 a 和 b 的数量的奇偶性)。
然而,严格的正则表达式的推导相当混乱;环顾四周简化了我们的思维方式。
-
(?=(?:b*ab*a)*b*$) 是一个前瞻,(由于 ^ 和 $ 的影响)检查 whole 字符串是否匹配 (b*ab*a)*b* - 这意味着偶数个 a , b 在两者之间和周围是可选的和有限的。
-
(?=(?:a*ba*b)*a*ba*$) 是一个前瞻,检查 whole 字符串是否与 (a*ba*b)*a*ba* 匹配 - 这意味着 b 的奇数个,a 可选且有限多个介于两者之间和周围。
我们需要 3 次遍历字符串来检查字符串是否满足上述正则表达式的给定属性。
使用 POSIX 正则表达式,您可以针对这 2 个正则表达式测试一个字符串 2 次以断言 2 个属性:
^(b*ab*a)*b*$
^(a*ba*b)*a*ba*$
但由于存在严格的正则表达式并且可行,因此存在单个 POSIX 正则表达式来匹配满足属性的字符串。请阅读 this post 了解将 DFA 转换为严格正则表达式的方法。
问题 2
^(?!.*a.*b.*b).*$
只是向前看,检查没有办法匹配子序列abb。
由于没有指定字母表,它可能被解释为任何和所有字符,用严格的正则表达式编写它是可能的,但不切实际。但是,如果允许否定字符类,则可以轻松编写:
([^a]*a[^b]*b[^b]*|[^a]*a[^b]*|[^a]*)
(上面是严格的正则表达式,但是可以很容易地改写为POSIX正则表达式)
交替的 3 项(从右到左)对应于 DFA 中的 3 个状态:尚未找到 a;前面有a,但没有b;后面有一个a 和b,但还没有找到第二个b。 DFA中还有一个陷阱状态(不可能转换到目标状态):找到了子序列abb。
问题 3
^(?!\d*(\d)\d*\1)\d+$
使用负前瞻,我们将只检查是否没有匹配项可以找到重复的字符。由于这是一个数字字符串,因此我不允许通过指定至少 1 个数字 \d+ 来匹配空字符串。这个正则表达式的关键是使用捕获的组和反向引用。
同样,对此存在严格的正则表达式,但这是不切实际的,因为编写这样的正则表达式几乎等同于一一列出所有可能的匹配项。总共有 Sum [i = 1..10] ( 10! / i! ) 个可能的匹配项(允许前导 0)。没有办法绕过它,因为是否允许下一个字符在很大程度上取决于到目前为止已处理的所有字符。
这在 POSIX 正则表达式中也是不切实际的,因为没有否定捕获组的机制。
问题 4
无法为这个提供更好的正则表达式...
^(?!(?=.*(.).*\1).*((?!\1).).*\2)(?!.*(.)(?:.*\3){2})\d+$
失败有两种情况:数字重复超过 2 次,或者有 2 个不同的数字重复。
-
(?!(?=.*(.).*\1).*((?!\1).).*\2) 检查是否有 2 个不同的数字重复。由于((?!\1).),这种前瞻(?=.*(.).*\1) 挑选出一个重复的字符(至少两次),而.*((?!\1).).*\2 挑选出与之前检查的不同的数字,并尝试找到重复的数字。
-
(?!.*(.)(?:.*\3){2}) 检查一个数字最多重复两次。里面的模式尝试匹配一个数字的 3 个实例。
这与问题 3 有相同的问题,因此编写严格的正则表达式或 POSIX 正则表达式是不切实际的。如果您好奇,有Sum [i = 1..10] ( 10! / i! ) + Sum [i = 1..10] ( (i + 1)C2 * 10! / i! ) 可能的匹配项(允许前导 0)。
对于问题 3 和 4,尤其是问题 4,更实际的做法是检查字符串是否只包含数字,然后对数字进行排序并循环检查是否有重复的数字。