python 正则表达式:其中一个或两个都带有分隔符

python regex: either or or both with separator

我需要一个正则表达式来匹配 aba;b.

我不能写 a|b|a;b 因为 ab 包含命名组,如果我尝试这样做,我会得到一个异常: 重新定义组名'a'为第8组;是第 3 组第 60.

a;?b 也不起作用,因为 ab 不能匹配。

你会如何解决这个问题? re 库可以吗? 我听说还有一个图书馆叫pyparsing。那会更适合这个问题吗?


背景:这是 this one 的后续问题。 因为似乎不可能通过 urwid 或 curses 中的颜色代码,所以我正在尝试解码我​​从 git 获得的颜色代码,以便 urwid 可以重新编码这些颜色。

为避免复制和粘贴出现问题,我在以下正则表达式中省略了前导控制字符:

工作正则表达式,除了它不匹配 [1m(粗体),它在 a test program:

中使用
reo_color_code = re.compile(
    r'\['
    r'((?P<series>[01]);)?'
    r'((?P<fgbg>[34])(?P<color>[0-7]))?'
    r'm'
)

不编译正则表达式:

reo_color_code = re.compile(
    r'\['
    r'('
        r'((?P<series>[01]))'
        r'|'
        r'((?P<fgbg>[34])(?P<color>[0-7]))'
        r'|'
        r'((?P<series>[01]));((?P<fgbg>[34])(?P<color>[0-7]))'
    r')'
    r'm'
)

抛出异常

re.error: redefinition of group name 'series' as group 8; was group 3 at position 60

在这种情况下我要做的不是尝试构建一个正则表达式来解决整个问题,而是我会实现如下方法(也使用 re 但在不同的级别):

def get_info(s):
    if s.startswith('[') and s.endswith('m'):
        p = s[1:-1]
        if ';' in p:
            m = re.match('^([01]);([34])([0-7])$', p)
        else:
            m = re.match('^([01])$|^([34])([0-7])$', p)
        if m:
            return tuple(m.groups())
    return None, None, None

您可以像这样使用它:

>>> serie, fgbg, color = get_info('[1;37m')
>>> serie, fgbg, color
('1', '3', '7')

PS: 没做太多测试。希望对你有帮助。

这里是一个更通用的用于破解 ANSI 终端序列的正则表达式:

\[(\d+)(?:;(\d+))?([a-z])

如果您想按名称访问群组,请使用:

\[(?P<d1>\d+)(?:;(?P<d2>\d+))?(?P<trailing>[a-z])

我没有给整数值任何有意义的名称,因为它们会根据尾随的字母字符而变化(并且也可以超过 1 位)。

对于未来的正则表达式开发工作,https://regex101.com 是一个很好的交互式页面,可以解决问题。

既然你询问了 pyparsing,这里是 pyparsing 解析器的样子:

import pyparsing as pp

integer = pp.pyparsing_common.integer
ansi_expr = ("[" 
             + integer("d1") 
             + pp.Optional(';' + integer("d2")) 
             + pp.oneOf(list(pp.alphas.lower()))("trailing"))

ansi_expr.runTests("""\
    [1m
    [23;34z
    """)

带有测试输出:

[1m
['[', 1, 'm']
- d1: 1
- trailing: 'm'

[23;34z
['[', 23, ';', 34, 'z']
- d1: 23
- d2: 34
- trailing: 'z'