何时问题对于正则表达式来说过于复杂?

时间:2008-10-23 16:55:55

标签: regex

请不要回答明显的问题,但有哪些限制标志告诉我们使用正则表达式不能解决问题?

例如:为什么正则表达式的完整电子邮件验证过于复杂?

13 个答案:

答案 0 :(得分:14)

正则表达式是finite-state automata的文本表示。也就是说,它们仅限于非递归匹配。这意味着您的正则表达式中不能包含“范围”或“子匹配”的任何概念。请考虑以下问题:

(())()

所有开放的parens是否都与关闭的paren相匹配?

显然,当我们将此视为人类时,我们可以很容易地看到答案是肯定的。但是,没有正则表达式能够可靠地回答这个问题。为了进行这种处理,您需要一个完整的pushdown automaton(就像带有堆栈的DFA)。这最常见于解析器的幌子,例如由ANTLR或Bison生成的解析器。

答案 1 :(得分:13)

需要注意的一些事项:

  1. 开始和结束标记检测 - 匹配配对
  2. 递归
  3. 需要倒退(虽然你可以扭转字符串,但这是一个黑客攻击)
  4. 正如我所爱的那样,正则表达并不擅长这三件事。请记住,保持简单!如果您正在尝试构建一个“一切”的正则表达式,那么you're probably doing it wrong

答案 2 :(得分:9)

当您需要解析未由regular language定义的表达式时。

答案 3 :(得分:7)

归结为使用常识。如果你想要匹配的东西变成了一个无法管理的怪物正则表达式,那么你需要将它分解成小的,逻辑的正则表达式,或者你需要开始重新思考你的解决方案。

获取电子邮件地址(根据您的示例)。这个简单的正则表达式(取自RegEx buddy)匹配99%的所有电子邮件:

\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}\b

这很简短,你很少会遇到问题。但是,正如RegEx buddy的作者指出的那样,如果您的电子邮件地址位于罕见的顶级域名“.museum”中,则不会被接受。

要真实匹配您需要遵守的RFC 2822标准所需的所有电子邮件地址。它概述了电子邮件地址可以格式化的多种方式,而且非常复杂。

以下是试图遵守RFC 2822的正则表达式示例:

(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"
(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x
0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9]
(?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.)
{3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08
\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])

这显然会成为收益递减的问题。最好使用容易维护的实现,该实现匹配99%的电子邮件地址,而不是接受99.9%的电子邮件地址的怪物。

正则表达式是程序员工具箱中的一个很好的工具,但它们不是解决所有解析问题的方法。如果您发现RegEx解决方案开始变得非常复杂,您需要尝试将其逻辑分解为较小的正则表达式以匹配部分文本,或者您需要开始查看其他方法来解决您的问题。同样,由于性质的原因,正则表达式无法解决问题(正如一张海报所说,不遵守Regular Language)。

答案 4 :(得分:6)

正则表达式适用于标记,查找或识别单个文本位,例如:在源代码中查找关键字,字符串,注释等。

正则表达式不适用于确定多个文本位之间的关系,例如使用正确配对的大括号查找源代码块。你需要一个解析器。解析器可以使用正则表达式来标记输入,而解析器本身可以确定不同正则表达式匹配的方式。

基本上,如果您开始考虑“平衡组”(.NET的捕获组减法功能)或“递归”(Perl 5.10和PCRE),那么您将使用正则表达式。

答案 5 :(得分:4)

以下是Raymond Chen的一句好话:

  

不要让正则表达式做他们不擅长的事情。如果要匹配简单模式,则匹配简单模式。如果你想做数学,那就做数学。正如毛里求斯的评论者所说,“诀窍不是花时间开发组合锤/螺丝刀,而只需使用锤子和螺丝刀。

Source

答案 6 :(得分:3)

使用正则表达式解决问题,然后将其交给其他正在使用正则表达式的人。如果他们在大约10分钟内无法告诉你它的作用(或者至少可以肯定地说他们理解),那就太复杂了。

答案 7 :(得分:3)

确定停止使用正则表达式的标志是:如果你有许多分组括号'()'和许多替代品'|'那么你肯定会尝试用正则表达式做一个(复杂的)解析

添加到混合Perl扩展,反向引用等,很快您就会拥有一个难以阅读,难以修改且难以推断其属性的解析器(例如,此解析器将在哪个输入中工作一个指数时间)。

这是停止regexing并开始解析的时候(使用手工制作的解析器,解析器生成器或解析器组合器)。

答案 8 :(得分:2)

除了巨大的表达外,还有一些主要的限制,可以通过regexp来处理。 例如,你不能不为n chars a,n chars b描述的单词写regexp,其中n可以是任意,更严格alt text

在不同的语言中,regexp是Regular language的扩展,但解析的时间可能非常大,而且这段代码是不可移植的。

答案 9 :(得分:1)

每当你无法确定它是否真的能解决问题时,例如:

  • HTML解析
  • 电子邮件验证
  • 语言解析器

特别是当已经存在以完全可理解的方式解决问题的工具时。

正则表达式可以在我提到的域中使用,但仅作为整个问题的子集和特定的简单案例。

这超出了正则表达式(常规语言+扩展)的技术限制,在大多数情况下,可维护性和可读性限制早于技术限制。

答案 10 :(得分:0)

在编写解决方案后问题的约束可能会发生变化时,正则表达式的问题过于复杂。因此,在您的示例中,当您无权访问目标邮件系统以验证电子邮件地址是否附加到有效用户时,如何确定电子邮件地址是否有效?你不能。

答案 11 :(得分:-1)

我的限制是一个大约30-50个字符长的正则表达式模式(根据固定文本的数量和正则表达式命令的数量而变化)

答案 12 :(得分:-1)

这可能听起来很愚蠢,但我经常感叹不能使用正则表达式进行数据库类型的查询。现在特别多,因为我一直在搜索引擎上输入这些类型的搜索字符串。搜索+complex AND +"regular expression"

非常困难,甚至不可能

例如,如何在emacs中搜索名称中包含Buffer和Window的命令?我需要单独搜索.*Buffer.*Window and .*Window.*Buffer