regex - 有没有办法在正则表达式中定义自定义速记?

标签 regex

我有一个表单的正则表达式

def parse(self, format_string):
    for m in re.finditer(
        r"""(?: \$ \( ( [^)]+ ) \) )   # the field access specifier
          | (
                (?:
                    \n | . (?= \$ \( ) # any one single character before the '$('
                )
              | (?:
                    \n | . (?! \$ \( ) # any one single character, except the one before the '$('
                )*
            )""",
        format_string,
        re.VERBOSE):
    ...

我想用一些自定义速记“常量”替换所有重复序列( \$ \( ),如下所示:

def parse(self, format_string):
    re.<something>('\BEGIN = \$\(')
    for m in re.finditer(
        r"""(?: \BEGIN ( [^)]+ ) \) )   # the field access specifier
          | (
                (?:
                    \n | . (?= \BEGIN ) # any one single character before the '$('
                )
              | (?:
                    \n | . (?! \BEGIN ) # any one single character, except the one before the '$('
                )*
            )""",
        format_string,
        re.VERBOSE):
    ...

有没有办法用正则表达式本身来做到这一点(即不使用 Python 的字符串格式将 \BEGIN 替换为 \$\( )?

澄清: Python 源代码仅用于上下文和说明。我正在寻找 RE 解决方案,该解决方案在某些 RE 方言(可能不是 Python 的方言)中可用,而不是专门针对 Python 的解决方案。

最佳答案

我认为这在 Python 的正则表达式风格中是不可能的。您将需要仅由 PCRE 支持的递归(或更确切地说是模式重用)。事实上,PCRE 甚至在其 man page 中提到了定义速记是如何工作的。 (搜索“定义子模式”)。

在 PCRE 中,您可以以与反向引用类似的方式使用递归语法 - 除了再次应用该模式,而不是尝试从反向引用获取相同的文字文本。例子:

/(\d\d)-(?1)-(?1)/

匹配日期之类的东西(其中 (?1) 将被替换为 \d\d 并再次评估)。这真的很强大,因为如果您在引用组本身中使用此构造,您将获得递归 - 但我们在这里甚至不需要它。以上也适用于命名组:
/(?<my>\d\d)-(?&my)-(?&my)/

现在我们已经很接近了,但定义也是模式的第一次使用,这使表达式有些困惑。诀窍是首先在从未评估过的位置使用模式。手册页建议了一个依赖于(不存在的)组的条件 DEFINE :
/
(?(DEFINE)
  (?<my>\d\d)
)
(?&my)-(?&my)-(?&my)
/x

构造 (?(group)true|false)适用模式 true若群group之前使用过,和(可选)模式 false除此以外。由于没有组DEFINE ,条件将始终为假,并且 true模式将被跳过。因此,我们可以将各种定义放在那里,而不必担心它们会被应用并弄乱我们的结果。这样我们就可以让它们进入模式,而无需真正使用它们。

而替代方案是一个负前瞻,它永远不会到达定义表达式的点:
/
(?!
  (?!)     # fail - this makes the surrounding lookahead pass unconditionally
  # the engine never gets here; now we can write down our definitions
  (?<my>\d\d) 
)
(?&my)-(?&my)-(?&my)
/x

但是,如果您没有条件,但确实有命名模式重用(我认为不存在这样的风格),那么您只需要这种形式。另一个变体的优点是使用 DEFINE使组的用途一目了然,而前瞻方法有点模糊。

所以回到你原来的例子:
/
# Definitions
(?(DEFINE)
  (?<BEGIN>[$][(])
)
# And now your pattern
  (?: (?&BEGIN) ( [^)]+ ) \) ) # the field access specifier
|
  (
    (?: # any one single character before the '$('
      \n | . (?= (?&BEGIN) ) 
    )
  | 
    (?: # any one single character, except the one before the '$('
      \n | . (?! (?&BEGIN) ) 
    )*
  )
/x

这种方法有两个主要注意事项:
  • 递归引用是 atomic .也就是说,一旦引用匹配了某些东西,它就永远不会被回溯到。在某些情况下,这可能意味着您必须巧妙地设计表达方式,以便第一个匹配项始终是您想要的匹配项。
  • 您不能在定义的模式内使用捕获。如果你使用类似 (?<myPattern>a(b)c) 的东西并重复使用它,b永远不会被捕获——当重用一个模式时,所有的组都是非捕获的。

  • 然而,与任何类型的插值或串联相比,最重要的优势是,您永远不会产生无效模式,并且您也不会弄乱您的捕获组计数。

    关于regex - 有没有办法在正则表达式中定义自定义速记?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18149814/

    相关文章:

    regex - 如何在 Windows 的 cmd 脚本中用 sed 替换 <> 字符?它们被解释为重定向运算符

    javascript - 将 2 个正则表达式合并为一个

    PHP:Is_numeric 在 0 上返回 false

    r - 扩展 gsub 和 grepl 以忽略给定分隔符之间的子字符串

    Python,正则表达式动态计数

    python - 表达式在 Windows 上匹配,但在 Mac 上不匹配

    c# - 这个科学计数法在 C# 中的正则表达式如何?

    javascript - 如何替换字符串中所有但第一次出现的模式

    regex - 如何为特定格式构建正则表达式,在版本中效果很好

    java - 为什么我的正则表达式找不到我的模式的所有实例