【问题标题】:Regex with multiple optional groups具有多个可选组的正则表达式
【发布时间】:2014-12-30 19:14:35
【问题描述】:

我进行了搜索,但找不到有效的解决方案。我正在为 Excel 文件的标题格式编写正则表达式。这些使用 &-commands 来格式化页眉和页脚,然后将左、中和右页眉简单地连接在一起:

(ECMA 规范中的¶18.3.1.39)

&L&"Lucida Grande,Standard"&K000000Left top&C&"Lucida Grande,Standard"&K000000Middle top&R&"Lucida Grande,Standard"&K000000Right top

这三个部分都是可选的。根据我所读到的关于使组成为可选的内容,我提出了以下正则表达式(Python 风格):

re.compile(r"""
(?P<left>&L.+?)
(?P<center>&C.+?)
(?P<right>&R.+?)$
""", re.VERBOSE)

但它失败了一个简单的字符串只包含一个部分&amp;Ltest header。我想我理解潜在的问题——缺少可选组的模式会影响其他模式——但不是语法,或者更确切地说,当缺少可选组时会发生什么。

【问题讨论】:

  • 将所有组设为可选有什么问题?
  • 你使用什么语言?
  • @AvinashRaj 所有组都是可选的。
  • @sin Python,但这并不重要。

标签: regex


【解决方案1】:

试试

^(?:.*?(?P<left>&L.[^&]*))?(?:.*?(?P<center>&C.[^&]*))?(?:.*?(?P<right>&R.[^&]*))?.*$

regex101 demo.


left 组的解释(centerright 几乎相同):

(?:
    .*? # consume any preceding text
    (?P<left> # then capture...
        &L # "&L" literally
        . # the character after that
        [^&]* # and then everything up to the next "&" character
    )
)? # and make the whole thing optional.

P.S.:您的模式没有使任何组成为可选的。您应该将? 放在 组之后,例如(?P&lt;left&gt;&amp;L.+)?


更新

由于组不应在下一个 &amp; 字符处结束,您可以尝试该模式

(?P<left>&L.+?)?(?P<center>&C.+?)?(?P<right>&R.+?)?$

相反。我所做的只是通过添加? 使所有组成为可选组,并通过将锚点$ 放在末尾来强制模式消耗整个字符串。

regex101 demo.

更新(?:&amp;L(?P&lt;left&gt;.+?))?(?:&amp;C(?P&lt;center&gt;.+?))?(?:&amp;R(?P&lt;right&gt;.+?))?$ 不会捕获 &amp;L&amp;C&amp;R 位。

【讨论】:

  • &amp; 命令包含在每个部分中,因此限制性太强。
  • @CharlieClark:那么这些小组会在哪里结束?
  • 与下一组的开始或文本的结尾。这是一种可怕的格式。
  • 谢谢,现在可以工作了(基本上我已经写了,除了结尾的 $)。将 $L、&C 和 &R 排除在捕获组之外的最佳方法是什么?展望未来?
  • @CharlieClark:不,您只需将它移到该组前面,然后将整个内容包含在另一个非捕获组中。查看更新。
【解决方案2】:

您可以使用与 left/center/right 匹配的正则表达式和一系列交替。
条件用于匹配部分,无论它们在行中出现的顺序如何。
这样就可以匹配其中的 1、2 或 3 个。

更新

已修改以匹配每个部分,直到下一个部分(如果存在)。
基于这里的条件信息 -> http://www.rexegg.com/regex-conditionals.html

如果它的 python/PCRE 这应该可以工作:

(?:(?:[^&]|&[\S\s])*?(?:&L(?P<left>(?(left)(?!))(?:[^&]|&[^LCR])*)|&C(?P<center>(?(center)(?!))(?:[^&]|&[^LCR])*)|&R(?P<right>(?(right)(?!))(?:[^&]|&[^LCR])*))){1,3}  

如果它的 Perl/PCRE,这可行:

  # (?:(?:[^&]|&[\S\s])*?(?:&L(?<left>(?(<left>)(?!))(?:[^&]|&[^LCR])*)|&C(?<center>(?(<center>)(?!))(?:[^&]|&[^LCR])*)|&R(?<right>(?(<right>)(?!))(?:[^&]|&[^LCR])*))){1,3}

 (?:
      (?: [^&] | & [\S\s] )*?       # Get all possible quoted &&
                                    # even &[LCR] if needed
      (?:                           # Get one of   &L or &C or &R
           &L
           (?<left>                      # (1), Left
                (?(<left>)
                     (?!)                          # Allow only 1 left
                )
                (?: [^&] | & [^LCR] )*        # Get all possible quoted && up to but not &[LCR]
           )
        |  
           &C
           (?<center>                    # (2), Center
                (?(<center>)
                     (?!)                          # Allow only 1 center
                )
                (?: [^&] | & [^LCR] )*
           )
        |  
           &R
           (?<right>                     # (3), Right
                (?(<right>)
                     (?!)                          # Allow only 1 right
                )
                (?: [^&] | & [^LCR] )*
           )
      )
 ){1,3}                        # Do 1 to 3 times

输出:

 **  Grp 0 -  ( pos 0 , len 132 ) 
&L&"Lucida Grande,Standard"&K000000Left top&C&"Lucida Grande,Standard"&K000000Middle top&R&"Lucida Grande,Standard"&K000000Right top  
 **  Grp 1 -  ( pos 2 , len 41 ) 
&"Lucida Grande,Standard"&K000000Left top  
 **  Grp 2 -  ( pos 45 , len 43 ) 
&"Lucida Grande,Standard"&K000000Middle top  
 **  Grp 3 -  ( pos 90 , len 42 ) 
&"Lucida Grande,Standard"&K000000Right top  

【讨论】:

  • 这看起来可能会奏效(这可能是多么棘手)。我试试看,谢谢。
  • @CharlieClark - 更新了答案。
  • 非常感谢您的更新。我目前正在使用更简单的形式,因为它就足够了,但是由于微妙之处(仅限于一场比赛、与订单无关等),您的解决方案非常具有指导意义。
猜你喜欢
  • 2011-06-25
  • 2017-06-21
  • 2023-03-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多