对于正则表达式模式,如何确定与模式匹配的最长字符串的长度?

Posted

技术标签:

【中文标题】对于正则表达式模式,如何确定与模式匹配的最长字符串的长度?【英文标题】:For a regex pattern, how to determine the length of longest string that matchs the pattern? 【发布时间】:2015-09-19 06:02:21 【问题描述】:

拥有一个正则表达式模式regexPattern,我如何确定与regexPattern 匹配的最长字符串的长度。

想象中的int LongestLength(string pattern) 应该是这样工作的:

Assert.Equals(LongestLength("[abc]"), 1);
Assert.Equals(LongestLength("(a|b)c?"), 2);

Assert.Equals(LongestLength("d*"), int.MaxValue); // Or throws NoLongestLengthException

虽然问题是在 C# 中,但 C# 和 javascript 的答案都很好。

【问题讨论】:

除了最简单的正则表达式之外,甚至很难弄清楚哪些字符串可以匹配,更不用说可能的最长字符串了。您要的是魔术或蛮力。两者都不能解决问题。 “我怎么猜” - “猜”是什么意思? @nnnnnn 我的意思是如何确定或找出。抱歉,我刚刚更正了问题。 将正则表达式转换为 DFA?这显然不是一件小事。 您想处理任何 C#(或 javascript)正则表达式吗?如果你稍微限制一下可能性,那就容易多了。 (反向引用会特别烦人)。 【参考方案1】:

只使用运算符?*+|,加上括号、字符类,当然还有普通字符,这对于proper regex 来说非常简单。事实上,即使是\1 样式的反向引用(它不是正则表达式的正式定义的一部分,并且确实使一些关于正则表达式的问题复杂化)也可以毫无问题地处理。

正则表达式只是树结构的紧凑编码(类似于数学公式是描述算术的树结构的紧凑编码)。在每对相邻的字符之间,都有一个隐含的“跟随”运算符,对应于具有 2 个子节点的节点,一个是其左侧的子正则表达式,另一个是正则表达式的整个其余部分;由| 字符分隔的子正则表达式序列对应于单个“alt”节点,其子节点与备选节点一样多(即,比| 字符的数量多一个),而每个其他运算符只有一个子节点(即它操作的子正则表达式),并且每个普通字符或字符类根本没有孩子。要计算最大长度匹配字符串,您可以对这个树结构进行自下而上的遍历,在每个节点贪婪地分配与该节点匹配的最长字符串的长度,给定与其匹配的最长字符串的知识孩子们。

决定匹配这棵树中任何给定节点的最长字符串长度的规则是:

follows(x, y) (xy): maxlen(x) + maxlen(y) alt(a, b, ..., z) (a|b|...|z): max(maxlen(a), maxlen(b), ..., maxlen(z)) 也许(x) (x?): maxlen(x) rep(x) (x*) 或 posrep(x) (x+):无穷大 任何其他单个字符或字符类 ([...]):1 \1-style 反向引用:对应括号表达式的 maxlen

一个后果是*+ 在任何地方的存在(除非被转义或显然是字符类的一部分)将导致无穷大向上传播直到它到达根。

示例

Regex: abcd
"Function call syntax": follows(a, follows(b, follows(c, d)))
As a tree:

follows
 /  \
a  follows
    /  \
   b  follows
       /  \
      c    d

第二个例子:

Regex: (a|b|de)c?
"Function call" syntax: follows(alt(a, b, follows(d, e)), maybe(c))
As a tree:

     follows
     /     \
   alt     maybe
  / | \        \
 a  b follows   c
       /  \
      d    e

对于第二个正则表达式/树,自下而上的遍历将为叶节点 a、b、d、e 和 c 分配 1 的 maxlen;那么底部 follow() 节点的 maxlen 为 1 + 1 = 2;那么 alt() 节点的 maxlen 是 max(1, 1, 2) = 2; Maybe 节点的 maxlen 为 1;最上面的 follow() 节点的 maxlen,因此对于整个正则表达式,是 2 + 1 = 3。

如果您指的是允许其他 Perl 样式增强功能的正则表达式,它可能会变得更加复杂,因为局部最优的长度选择可能会导致全局次优的长度。 (我曾认为 Perl 风格的扩展甚至有可能使正则表达式图灵完备,这意味着通常不可能确定是否有 any 匹配字符串 - 但the discussion here 似乎表明情况并非如此,除非您当然允许在 ?... 构造中。)

【讨论】:

这与 初始分析 相同,当长度保证找不到匹配时,正则表达式引擎可能会优化正则表达式以快速失败。 Java 使用类似的分析来检查后向模式的长度以拒绝 inf。长度模式(尽管有一个长期存在的错误)。无论如何,除了理论正则表达式的语法之外,没有可靠的方法来判断最长匹配字符串的长度。 @nhahtdh:像我在这里所做的那样,通过确定 最长 可能的匹配,没有什么可以排除的。您有时可以通过执行类似的计算来计算可能的最短匹配来排除匹配。 啊,是的,你是对的。最长可能匹配仅用于分析后向模式。最短可能匹配用于初始长度检查。【参考方案2】:

所以我将如何执行此功能是首先创建键值对数据类型。然后用每种正则表达式语法填充数据类型。所以关键是正则表达式语法(例如:“*”)。该值将是它将增加多少匹配的字符串的可能长度。所以键:“*”的值为 int.maxvalue。因此,您将遍历您的列表并搜索传入的任何语法的正则表达式,并总结您找到的所有值并返回它。但是,您必须记住某些语法已转义,因此您无法计算它们。以及一些语法自动使可能的长度为 int.maxvalue(“*”、“+”等)。因此,请先检查这些语法,以便在找到这些类型的正则表达式语法后立即自动发回 int.maxvalue。

【讨论】:

以上是关于对于正则表达式模式,如何确定与模式匹配的最长字符串的长度?的主要内容,如果未能解决你的问题,请参考以下文章

正则表达式与 C# 中的 OR 条件最长匹配

正则表达式贪婪与非贪婪比较

R 模式匹配与正则表达式的子集 data.table

python正则表达式贪婪算法与非贪婪算法与正则表达式子模式的简单应用

浅谈正则表达式匹配模式—懒惰模式

匹配模式的正则表达式,或者是一个空字符串