正则表达式的正则表达式?

9

可能是重复问题:
是否有一个正则表达式来检测有效的正则表达式?
查找正则表达式的正则表达式?

我有一个应用程序,允许用户输入正则表达式。如何检查任何正则表达式输入并确保它们是有效的,因为如果它们无效,则会出现preg_match错误?

我不想在preg_match之前使用“@”,所以如果有一种方法可以检查用户输入的正则表达式的有效性,那就太好了。

PHP的正则表达式系统似乎对我来说过于复杂,难以为其编写正则表达式。


3
根据应用程序的目的,您可能希望在输入正则表达式时设置超时。用户很容易故意或意外地输入会导致大量服务器资源被耗费的回溯正则表达式。 - Martin Smith
5个回答

30

在数学上,使用正则表达式无法验证一个正则表达式。这是因为(正式的)正则表达式只能识别 正则语言。语言是任何一组字符串。例如,所有十进制数的集合是一种语言(顺便说一下,可以使用正则表达式描述),所有有效正则表达式的集合也是一种语言。正则 语言是仅需要 固定有限内存(不随输入大小变化的函数)才能识别的语言。

包含所有有效正则表达式的语言不是正则语言,因此使用正则表达式无法识别一个正则表达式。

要理解这一点,请注意正则表达式中包含必须匹配的括号。因此,如果出现了“(”,那么后面必须出现“)”。这是不可能用具有固定有限内存的机器来描述的。因为,如果有一种方法可以做到这一点,并且您的正则表达式具有K个不同状态的有限内存(对于某个整数K),则由K个开括号后跟K个闭括号组成的表达式,虽然是一个有效的正则表达式,但无法被该机器识别——这是一个矛盾(请注意,在形式语言中,我们的假设是文本处理每次处理一个字符,从左到右,这与应用正则表达式相同)。我们把描述正则表达式的这种语言称为上下文无关语言而不是正则语言。

(使用泵引理可以轻松证明正则表达式不构成正则语言)

因此,在识别正则表达式时存在一个基本的计算机科学问题:数学上不可能使用正则表达式进行识别。

有限状态自动机可以识别正则语言,即具有有限状态但没有内存的机器。要解决您的问题,您需要添加一些与输入大小相关的内存。由于正则表达式是上下文无关的(幸运的是它们不是某种晦涩难懂的语言),因此可以使用下推自动机在线性时间内识别。这是一个“for”循环,一次一个标记(通常是字符),并在堆栈上跟踪看到的内容,即以先进后出的方式“推入”数据,稍后再“弹出”数据。 (将数据推送到堆栈的示例:“我需要记住稍后找到匹配的`)`!”;您可以按需多次“推”它;当您需要检查是否实际上需要已匹配前面的开括号时,稍后可以“弹出”它)。

当然,编写自己的正则表达式识别引擎可能会有点负担-但是如果您想这样做,应该知道上述限制。更明智的做法是使用已经存在的机制来处理它--我怀疑您可以将该工作交给正则表达式库或更善于处理正则表达式的语言,如Perl;但是@-方法听起来也不算太糟糕的想法:它可能很慢,但是您的用户可能会输入非常慢的正则表达式;而且这可能是一种不好的做法,但在您的情况下,似乎是可用的最佳解决方法。

有关更多信息,请参见维基百科中的以下文章:

  • 维基百科:下推自动机
  • 维基百科:无上下文语言
  • 维基百科:正则语言的泵引理
  • 维基百科:LIFO
  • 希望这有所帮助!


    3
    +1:回答有点长,但这是唯一正确的答案。在PCRE中,正则表达式语法是一种无上下文语言。另外:考虑将您的文章参考链接直接链接到维基百科本身 :-)(如果我错过了您已经这样做的忍者编辑,对不起) - Platinum Azure
    谢谢您的建议!我在“实现和运行时间”部分下的“正则表达式”文章中添加了一个小段落,并引用了这篇帖子 :) - dionyziz
    1
    由正则表达式描述的语言不一定是正则的(至少如果我们谈论的是现实世界中的正则表达式,而不是数学构造);具体来说,在PCRE中的反向引用可以用于描述像a^n b^m a^n这样的不规则语言。一些PCRE变种允许递归、条件子模式、原子组等。此外,PCRE不是上下文无关语言,因为命名子组不能重复使用名称。 - Tgr
    Tgr:这正是我为什么说“正式的正则表达式”的原因。要正式研究PCRE,就必须为其构建一个正式语言——如果你问我,这样做太麻烦了,得不偿失。无论如何,PCRE在最好的情况下也只是上下文无关的(如果我们选择仅使用其中一些常见方面),因此它肯定不能使用具有固定大小内存的有限状态自动机进行解析,结果仍然成立:无法使用正则表达式验证正则表达式。 - dionyziz

    10

    preg_match()如果发生错误会返回FALSE

    1. 将表达式发送到服务器。
    2. 对空字符串执行preg_match
    3. 查看是否出错。

    您可以使用Ajax进行实时验证,或者在提交表单后进行验证。
    您也可以尝试通过将表达式传递给JavaScript正则表达式引擎来进行验证,但是JavaScript的正则表达式语法不完全兼容PHP的语法。


    3
    请确保使用类型安全的比较:preg_match(…) === false - Gumbo
    1
    只是澄清一下:这个回答正确地回答了“有没有一种方法来检测有效的正则表达式?”这个问题。如果你想要一个正则表达式,那是不可能的,正如@dionyziz所解释的那样。 - nickie

    6

    让用户提交正则表达式几乎肯定是个坏主意。

    有些表达式非常昂贵。请尝试以下内容:

    preg_match('/(.*){1,32000}[bc]/','aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa')
    

    仅仅30个字符的输入就能导致这样的结果!它们看起来并不都像这样:/^(?:(\d+)|::)*$/ 在PCRE中也是指数级的时间复杂度。


    2

    首先想到的方法是在调用preg_match($sanatized_user_regex, "");后使用preg_last_error()。如果返回的不是PREG_NO_ERROR,则响应相应的错误消息。


    2
    你的问题有点模糊。您是要验证正则表达式的“语法”,还是确保正则表达式在应用于字符串后实际解析出内容。无论哪种情况,您都应该将验证留给用户(例如提供一个调试/文本框,他们可以在其中输入与其正则表达式匹配的字符串。如果正则表达式存在问题或找不到匹配项,则显示“未找到”错误)。
    在验证正则表达式本身方面,您可能希望从简单的验证器开始,检查只有有效字符(例如正则表达式语法的一部分,如$,^ \t等)是其正则表达式的一部分,但我认为尝试验证正则表达式中的逻辑结构可能相当复杂。也许有一些库可以验证正则表达式语法,但我不知道任何。

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接