可以使用条件来配对平衡组元素吗?

时间:2015-01-07 20:11:17

标签: regex recursion pcre

TL; DR:有没有办法指定条件,以便开放元素必须匹配其配对的结束元素?

示例位于on regex101.com

=====

正则表达式中的平衡元素通常通过递归来处理。这意味着可以找到嵌套的{...{...{...}...}...}

此外,PCRE允许(?(DEFINE)...)构造,它允许您定义各种模式而无需实际开始匹配。

在正则表达式中

# Define the opening and closing elements before the recursion occurs
(?(DEFINE)
  (?<open_curly>\{)
  (?<close_curly>\})
  # ... other definitions here ...

  (?<open>\g'open_curly')
  (?<close>\g'close_curly')
)

# Match the opening element
(\g'open'
  (?>
    # For recursion, don't match either the opening or closing element
    (?!\g'open'|\g'close')(?s:.)
  |
    # Recurse this captured pattern
    (?-1)
  )*

# Match the closing element
\g'close')

元素是{}个字符,可以与

等模式匹配
{{{}}}
{ test1 { test2 { test3 { test4 } } } }

我想要包含其他打开/关闭元素,例如[],或--[--],所以请在(?(DEFINE))中加入:

(?<open_square>\[)
(?<close_square>\])
(?P<open_pascal>(?i:\bbegin\b))
(?P<close_pascal>(?i:\bend\b))
(?P<open_lua>--\[)
(?P<close_lua>--\])
(?<open>\g'open_curly'|\g'open_square'|\g'open_pascal'|\g'open_lua')
(?<close>\g'close_curly'|\g'close_square'|\g'close_pascal'|\g'close_lua')

这个DOESN做的正确是将开放元素与其结束元素配对,允许--[}分组,这是不可取的。

有没有办法在像这样的正则表达式中创建开/关对?

2 个答案:

答案 0 :(得分:2)

你弄错了。您可以通过这样的递归实现它:

(?(DEFINE)
  (?<curly>  \{        \g<content>*? \}      )
  (?<square> \[        \g<content>*? \]      )
  (?<pascal> \bbegin\b \g<content>*? \bend\b )
  (?<lua>    --\[      \g<content>*? --\]    )

  (?<nested> \g<curly> | \g<square> | \g<pascal> | \g<lua> )

  (?<content>
    # Math non-recursive content (atomically)
    (?: (?! [{}\[\]] | \bbegin\b | \bend\b | --[\[\]] ) . )++
    # Or recurse
    | \g<nested>
  )
)

\g<nested>

Demo

这里的技巧是利用正则表达式引擎堆栈来保留匹配的开头符号的内存,以便在正则表达式引擎在离开时展开堆栈时需要匹配的结束符号递归。您还需要确保 catch-all .不会使用群组开始/结束符号。


更简单的案例更具可读性:

(?(DEFINE)
  (?<curly>  \{ \g<content>*? \} )
  (?<square> \[ \g<content>*? \] )

  (?<nested> \g<curly> | \g<square> )

  (?<content> [^{}\[\]]++ | \g<nested> )
)

\g<nested>

Demo

注意原子组。它可以防止过度回溯。此外,这个正则表达式立即消耗任何无法递归的东西,而不是先尝试递归它。


另外,要回答你的问题,条件在这里没用,因为你需要一个堆栈来跟踪你需要匹配的结束符号。

某些实现(PCRE)允许您通过递归模式使用堆栈,而其他实现(.NET)通过balancing groups公开堆栈。但是当你有几种平衡结构时,递归就是明显的赢家。

答案 1 :(得分:1)

我想说,用一堆命名组和
来污染逻辑是没有用的 不可预测的不必要的递归。

维持正确递归有三个主要部分(如下所示) 要做得对,你必须解析一切,所以你必须考虑到 内容和不平衡的错误。

引擎不会让你捕捉到第一级以外的任何细节 这意味着它更容易维护 TOP ,您可以从中获取信息和 CORE
你不能维护,但递归。一些微妙的差异,但可以看出 在下面的例子中。

任何时候核心被递归,它立即被个人包围 设置(对)独特的分隔符。这是为了正确展开堆栈。
这个过程不能被考虑(一般化)。

<强>更新
通常在递归函数中调用此正则表达式,每次都将 CORE 传递给它。
示例伪代码:

bool bIsOk = true;
bool RecurseCore( string core )
{  
     while( regex_search ( core, regex, match ) )
     {
          if ( match[1].success ) { print 'content' }
          else
          if ( match[2].success ) { print 'square'; bIsOk = RecurseCore( match[2].value ) }
          else
          if ( match[3].success ) { print 'curly'; bIsOk = RecurseCore( match[3].value ) }
          else
          if ( match[4].success ) { print 'pascal'; bIsOk = RecurseCore( match[4].value )  }
          else
          if ( match[5].success ) { print 'lua'; bIsOk = RecurseCore( match[5].value )   }
          else
          if ( match[6].success ) { print 'error'; bIsOk = false } // error
          if ( bIsOk == false ) { break }
     }
     return bIsOk;
 }

正则表达式:

 # //////////////////////////////////////////////////////
 # // The General Guide to 3-Part Recursive Parsing
 # // ----------------------------------------------
 # // Part 1. CONTENT
 # // Part 2. CORE
 # // Part 3. ERRORS

 (?si)                      # Dot all, no case

 (?:
      (                          # (1), Take off CONTENT
           (?&content) 
      )
   |                           # OR
      \[                         # Square's delimiter
      (                          # (2), square CORE
           (?= . )
           (?&core) 
        |  
      )
      \]                         # End-Delimiter
   |                           # OR
      \{                         # Curly's delimiter
      (                          # (3), curly CORE
           (?= . )
           (?&core) 
        |  
      )
      \}                         # End-Delimiter
   |                           # OR
      \b begin \b                # Pascal's delimiter
      (                          # (4), pascal CORE
           (?= . )
           (?&core) 
        |  
      )
      \b end \b                  # End-Delimiter
   |                           # OR  
      --\[                       # Lua's delimiter
      (                          # (5), lua CORE
           (?= . )
           (?&core) 
        |  
      )
      --\]                       # End-Delimiter
   |                           # OR
      (                          # (6), Take off Unbalanced (delimeter) ERRORS
           \b 
           (?: begin | end )
           \b 
        |  -- [\[\]] 
        |  [\[\]{}] 
      )
 )

 # ///////////////////////
 # // Subroutines
 # // ---------------

 (?(DEFINE)

      # core
      (?<core>
           (?>
                (?&content) 
             |  
                \[                         
                (?:                        # Square delimiter
                     (?= . )                    # recurse core
                     (?&core)                   
                  |  
                )
                \]
             |                           # OR
                \{
                (?:                        # Curly delimiter
                     (?= . )                    # recurse core 
                     (?&core) 
                  |  
                )
                \}      
             |                           # OR
                \b begin \b 
                (?:                        # Pascal delimiter
                     (?= . )                    # recurse core 
                     (?&core) 
                  |  
                )
                \b end \b     
             |                           # OR
                --\[
                (?:                        # Lua delimiter
                     (?= . )                    # recurse core 
                     (?&core) 
                  |  
                )
                --\]      
           )+
      )

      # content 
      (?<content>
           (?>
                (?!
                     \b 
                     (?: begin | end )
                     \b 
                  |  -- [\[\]] 
                  |  [\[\]{}] 
                )
                . 
           )+
      )

 )