【问题标题】:Capturing repeated pattern in Python在 Python 中捕获重复的模式
【发布时间】:2020-04-15 08:03:45
【问题描述】:

我正在尝试为 Python 日志格式化程序实现某种类似于降价的行为。

我们以这个字符串为例:

**This is a warning**: Virus manager __failed__

经过一些正则表达式后,字符串失去了类似 markdown 的语法,并被转换为 bash 代码:

\033[33m\033[1mThis is a warning\033[0m: Virus manager \033[4mfailed\033[0m\033[0m

但是应该压缩成

\033[33;1mThis is a warning\033[0m: Virus manager \033[4mfailed\033[0m

除了许多其他不起作用的解决方案之外,我尝试了这些:

(\\033\[([\d]+)m){2,} => 捕获:\033[33m\033[1m with g1 '\033[1m' and g2 '1' and \033[0m\033[0mwith g1 '\033[0m' and g2 '0'

(\\033\[([\d]+)m)+很多结果,不行

(?:(\\033\[([\d]+)m)+)很多结果,虽然如果我理解正确的话,这是重复模式的推荐方式,不行

还有其他……

我的目标是得到结果:

输入 \033[33m\033[1mThis is a warning\033[0m: Virus manager \033[4mfailed\033[0m\033[0m

输出

匹配 1 033[33m\033[1m

组 1:33

组 2:1

第 2 场比赛 033[0m\033[0m

组 1:0

组 2:0

换句话说,捕获那些“重复”的而不是单独的,这样我就可以将它们与正则表达式子融合。

【问题讨论】:

  • 有很多不必要的信息,你能发布一个示例输入和预期的输出吗?
  • 在最后... 输入:\033[33;1m这是一个警告\033[0m: 病毒管理器\033[4mfailed\033[0m 输出阅读消息的结尾跨度>
  • 由表达式创建的匹配组的数量将始终是一个设定值。例如(...)+ 将只生成一个匹配组。
  • 这不是 100% 清楚:规则是什么?可以\033[33m\033[1m\033[22m吗?如果是,预期的输出是什么?
  • 试试re.sub(r'(?:\\033\[\d+m){2,}', lambda m: r'\033['+";".join(set(re.findall(r"\[(\d+)", m.group())))+'m', text)

标签: python regex


【解决方案1】:

您想匹配连续重复的\033[\d+m 文本块,并用分号连接[ 之后的数字。

你可以使用

re.sub(r'(?:\\033\[\d+m){2,}', lambda m: r'\033['+";".join(set(re.findall(r"\[(\d+)", m.group())))+'m', text)

Python demo online

(?:\\033\[\d+m){2,} 模式将匹配两个或多个 \033[ 序列 + 一个或多个数字 + m 文本块,然后,匹配将传递给 lambda 表达式,其中输出将是:1 )\033[,2)[之后的所有数字用re.findall(r"\[(\d+)", m.group())提取并用set去重,然后3)m

【讨论】:

    【解决方案2】:

    问题中没有明确说明要修改的字符串中的模式。例如,033 是固定的还是025 甚至25?我在使用正则表达式时做了一些假设

    r" ^(\\0(\d+)\[\2)[a-z]\\0\2\[(\d[a-z].+)
    

    获取两个要合并的捕获组,用分号分隔。我试图在下面阐明我的假设,部分是为了帮助 OP 修改这个正则表达式以满足替代要求。

    Demo

    正则表达式执行以下操作:

    ^           # match beginning of line
    (           # begin cap grp 1
      \\0       # match '\0'
      (\d+)     # match 1+ digits in cap grp 2
      \[        # match '['
      \2        # match contents of cap grp 2
    )           # end cap grp 1
    [a-z]       # match a lc letter
    \\0         # match '\0'      
    \2          # match contents of cap grp 2
    \[          # match '['
    (\d[a-z].+) # match a digit, then lc letter then 1+ chars to the
                #   end of the line in cap grp 3
    

    如您所见,第 1 组中捕获的字符串部分是

    \033[33
    

    我假设这个字符串现在是033 的部分必须是两个或多个以零开头的数字,并且数字字符串的第二次出现由零之后的相同数字组成。这是通过在捕获组 2 中捕获 '0' (33) 之后的数字,然后使用反向引用 \2 来完成的。

    字符串的下一部分将被替换,因此不会被捕获:

    m\\033[
    

    我假设 m 必须是一个小写字母(或者应该是文字 m?),反斜杠和零以及必需的数字必须再次与捕获组 2 的内容匹配。

    字符串的剩余部分,

    1mThis is a warning\033[0m: Virus manager \033[4mfailed\033[0m\033[0m
    

    在捕获组 3 中被捕获。这里我假设它以一个数字开头(也许应该是 \d+),后跟一个不必与之前匹配的小写字母相同的小写字母(尽管这可以通过另一个捕获组来强制执行)。那时我将行的其余部分与.+ 匹配,放弃了该部分字符串中的匹配模式。

    一个也可以只有两个捕获组,现在是#2 的捕获组变成#1,而#2 是字符串中要被分号替换的部分。

    【讨论】:

    • 感谢您的详细解释!我会尽快测试并返回结果!
    【解决方案3】:

    对于您在此处描述的情况,这非常简单;只需从左到右写出您想要匹配和捕获的内容。在这里重复捕获块对您没有帮助,因为只会返回最近捕获的值。

    \\033\[(\d+)m\\033\[(\d+)m
    

    【讨论】:

    • 谢谢,但这仅对连续 2 次有效,而不是随机连续 N 次有效
    • 当然。我的观点是,你不是用一种表达方式和重复捕获组来做这件事的。您通常会使用一个表达式来获取整个序列,第二个(或字符串函数)从中获取数字列表,并使用一些逻辑来处理值 - 如 Wiktor Stribiżew 的回答。
    猜你喜欢
    • 2022-01-25
    • 2013-04-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多