对于正则表达式模式,如何确定与模式匹配的最长字符串的长度?
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。
【讨论】:
以上是关于对于正则表达式模式,如何确定与模式匹配的最长字符串的长度?的主要内容,如果未能解决你的问题,请参考以下文章