正则表达式匹配

Posted

技术标签:

【中文标题】正则表达式匹配【英文标题】:Regular expression matching 【发布时间】:2011-05-01 04:11:19 【问题描述】:

我想写一个任何之间的东西

()
(())
(()())
((()))
()()()

等等

【问题讨论】:

what the ... 1. 你的输入是什么(示例),2. 你想获取什么 3. 你用什么 RegEx 绑定了那个 didint 工作? 请澄清——介于什么之间?你想在那一串括号中找到什么? 我有一个条件,如果它匹配 () 或 (()) 或 ((())) 或 ()()() 或 (()()())。括号可以是任意数量。如果条件不匹配,我想进入错误部分。输入可以是上面提到的任何示例,一些错误的输入将是 (() 或 (()()( 或 (())) 等跨度> no only )( 不是一个有效的字符串 为什么 (() 无效?它匹配 "anything between ()" (在这种情况下,"anything" 是 "(") 【参考方案1】:

正则表达式无法有效处理这种嵌套结构。您需要的是语法和该语法的解析器。在您的情况下,语法很简单。如果您使用的是 python,请尝试 pyparsing 或 funcparserlib。

使用 pyparsing,您可以执行以下操作:

from pyparsing import nestedExpr
nestedExpr().parseString( "(some (string you) (want) (to) test)" ).asList()

这将返回一个包含嵌套字符串的已解析组件的列表。 nestedExpr 的默认分隔符是括号,因此您不必做任何额外的事情。如果你想使用 funcpasrerlib 你可以试试下面的

from funcparserlib.parser import forward_decl, many, a
bracketed = forward_decl()
bracketed.define(a('(') + many(bracketed) + a(')'))

之后你可以调用

bracketed.parse( "( (some) ((test) (string) (you) (want)) (to test))" )

它会在一个元组中返回解析后的元素。

【讨论】:

这是另一个错误的答案。仅仅因为 Python 无法进行必要的复杂模式匹配并不意味着所有其他语言都受到类似的阻碍。 Perl、php 和 PCRE 都可以很好地处理这个问题。看我的回答。 当您添加反向引用、所有格“+”或 (?number) 语法时,它不再是常规语言/表达式。它可以具有相似的语法,但这并不意味着它是同一件事。 Perl 5.10 及更高版本,如果我没记错的话,.Net 框架中的语言提供了这样的扩展。我认为您的模式中有一个错误,尽管我可能是错的,在那里单独评论。哦,我不能在那里发表评论。无论如何,我认为您拥有的 (?0) 应该是 (?1) 因为一旦匹配至少一个匹配的括号,您就应该递归。 不,在 (?0) 上递归整个括号是正确的。没有第 1 组可以按照所写的进行递归。我确实测试了所有这些。诚实的!我更喜欢将命名组用作一种正则表达式子例程的最终版本。它更容易阅读、调试、扩展和维护。希望这会有所帮助。 您对 (?0) 的看法可能是正确的。但是,如果您更喜欢可读性、可维护性、清晰性等,nestedExpr().parseString() 似乎更短更容易:),不管是不是正确的语言。【参考方案2】:

如果您被正则表达式语法不支持递归匹配的语言所困扰,我将为您提供我的简单 javascript 实现,您应该可以使用您选择的语言制作自己的语言:

function testBraces(s) 
    for (var i=0, j=0; i<s.length && j>=0; i++)
        switch(s.charAt(i)) 
            case '(':  j++ ; break; 
            case ')':  j-- ; break; 
        

    return j == 0;

你可以在这里玩它:http://jsfiddle.net/BFsn2/

【讨论】:

没错,j 永远不应低于零,因为它表示不平衡。 我只注意到 for 循环结束条件中的 &amp;&amp; j&gt;=0 位(是一直存在还是您在五分钟的窗口中对其进行了编辑?)。完美。 @Tim:它从一开始就存在,就像在演示中一样(在 jsFiddle 上)。 这个答案也是错误的:现代编程语言中的模式完全可以胜任。 我不知道某些正则表达式风格会进行递归模式匹配,因为在我使用的语言中,它们不会。大多数时候(阅读:关于我的雇主项目)我无法选择我使用的语言。不过我修正了我的答案。【参考方案3】:

所有这些声称您不能使用模式来匹配具有平衡嵌套括号的字符串的答案都是完全错误的。假装现代编程语言匹配的模式仅限于病态教科书意义上的“常规语言”是不切实际的。只要您允许反向引用,它们就不会。这使得现实世界的模式比教科书版本匹配得更多,使它们更加实用。

匹配平衡括号的最简单模式是\((?:[^()]*+|(?0))*\)。但是你应该永远不要写那个,因为它太紧凑了,不容易阅读。您应该始终使用/x 模式编写它以允许空格和 cmets。所以写成这样:

m
  \(              # literal open paren
     (?:          # begin alternation group
         [^()]*+  #  match nonparens possessively
       |          # or else
         (?0)     #  recursively match entire pattern
     )*           # repeat alternation group
  \)              # literal close paren
x

对于命名抽象以及将它们的定义及其执行顺序与它们的执行解耦,还有很多话要说。这导致了这种事情:

my $nested_paren_rx = qr

    (?&nested_parens)

    (?(DEFINE)

        (?<open>       \(       )
        (?<close>       \)      )
        (?<nonparens> [^()]     )

        (?<nested_parens>
            (?&open)
            (?:
                (?&nonparens) *+
              |
                (?&nested_parens)
            ) *
            (?&close)
        )

    )
x;

第二种形式现在可以包含在更大的模式中。

永远不要让任何人告诉你不能使用模式来匹配递归定义的东西。正如我刚刚演示的那样,您当然可以。

当您使用它时,请确保永远不要编写线路噪声模式。你不必,也不应该。禁止空格、cmets、子例程或字母数字标识符的编程语言是不可维护的。所以在你的模式中使用所有这些东西。

当然,确实有助于为此类工作选择正确的语言。 ☺

【讨论】:

@tchrist:如果您指定了您的示例可以使用的语言,那就太好了。当然,如果 OP 指定他希望用什么语言来实现它,那也很好。跨度> @avi:同意;因此我的最后一行。据我所知,递归模式适用于 Perl、PHP 和 PCRE。我使用的变量语法是针对 Perl 的,但这对问题来说有点偶然。我预计未来几年会有更多的语言采用递归模式,因为 PCRE 支持它们。不过要小心,因为 PCRE 对 head-vs-tail 递归的限制比 Perl 更多。 @tchrist,您关于“反向引用”使正则表达式不规则的说法是不正确的。反向引用只是 lots 替代方案的简写 - (.)\1 只是 aa|bb|cc|dd|... 的简写,您可以对反向引用的所有用途进行相同的转换。事实上,[...] 表示法和? 表示法都只是经典正则表达式中替代方案的简写。另一方面,递归正则表达式是一种非常不同的鱼,使用该功能会阻止它成为常规... @tobyodavies:考虑模式(.+).*\1。这需要超出自动机状态所需的辅助存储,并且实际上它需要与匹配的输入字符串的长度成比例的存储。这显然违反了 ʀᴇɢᴜʟᴀʀ 语言的基本属性之一,因此无法通过 DFA 解决,因为不需要进一步的存储,尤其是没有与输入长度成比例的存储。因此,根据定义,该模式描述的语言不是 ʀᴇɢᴜʟᴀʀ。 @tchrist,是的,然而,它与上下文无关,只有反向引用......因为它仍然无法匹配 S ::= '(' S ')' 我很想看看是否有人写过分析关于普通正则表达式可以解析的语言类别......(我仍然不认为递归正则表达式是“正常”功能......我真的没有看到它们在这篇文章之外使用)【参考方案4】:

祝你好运。你需要一个带有堆栈的有限状态自动机来解析这样的东西。仅使用正则表达式无法解析它,因为它不够强大。

【讨论】:

你会因为重复关于模式从根本上无法处理具有递归定义的语法的破格副歌而获得反对票。 Perl、PCRE 和 PHP 都可以很好地处理它们。学校坚持教授形式自动机理论,因为没有解释实际考虑导致许多工具使用反向引用甚至递归来扩展正则表达式,这是错误的。

以上是关于正则表达式匹配的主要内容,如果未能解决你的问题,请参考以下文章

正则表达式如何匹配空值?

正则表达式匹配规则

正则表达式匹配URL

正则表达式 .*+ 匹配啥?

正则表达式位置匹配攻略【转】

求教正则表达式如何匹配