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

Can conditionals be used to pair balance group elements?

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 } } } }

我想包含其他 open/close 元素,例如 [],或 --[--],因此请将那些包含在 (?(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')

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

有没有办法在这样的正则表达式中创建 open/close 对?

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

(?(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 . 不会消耗组 starting/ending 符号。


案例越简单,可读性越好:

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

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

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

\g<nested>

Demo

注意原子团。它可以防止过多的回溯。此外,此正则表达式会立即消耗任何不能递归的内容,而无需先尝试对其进行递归。


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

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

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

维护正确的递归分为三个主要部分(如下所列)。
要做到正确,您必须解析所有内容,因此您必须考虑
内容和不平衡的错误。

引擎不会让您详细捕捉第一级以外的任何内容。
这意味着更容易维护可以从中提取信息的 TOPCORE
你无法维护,但递归。一些细微的差异,但可以看出
在下面的例子中。

任何时候核心被递归,它立即被一个个体包围
一组(对)唯一的分隔符。这是为了正确展开堆栈。
这个过程不能分解(概括)。

更新
通常在递归函数中调用此正则表达式,每次将 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 
                  |  -- [\[\]] 
                  |  [\[\]{}] 
                )
                . 
           )+
      )

 )