Python 使用正则表达式查找和替换每个匹配项中任意数量的元素
我的目标是识别标记语言中的粗体括号文本,例如:Python 使用正则表达式查找和替换每个匹配项中任意数量的元素,python,regex,Python,Regex,我的目标是识别标记语言中的粗体括号文本,例如: [B] blah blah (foo) blah [/B] 并使用regex用另一个标记将其包围,如下所示: [B] blah blah [C](foo)[/C] blah [/B] 下面是我使用Python对此的尝试: outtext = re.sub(r'(\[B\].*?)(\(.*?\))(.*?\[/B\])', r'\1[C]\2[/C]\3', intext) 问题是,如果块中有多个带括号的字符串,则它不起作用: Input:
[B] blah blah (foo) blah [/B]
并使用regex用另一个标记将其包围,如下所示:
[B] blah blah [C](foo)[/C] blah [/B]
下面是我使用Python对此的尝试:
outtext = re.sub(r'(\[B\].*?)(\(.*?\))(.*?\[/B\])', r'\1[C]\2[/C]\3', intext)
问题是,如果块中有多个带括号的字符串,则它不起作用:
Input: [B] (foo) (bar) [/B]
Expected: [B] [C](foo)[/C] [C](bar)[/C] [/B]
Actual: [B] [C](foo)[/C] (bar) [/B]
我知道发生这种情况的原因,但我不知道如何解决它。是否可以更改我的正则表达式,以便它能够在每个块中查找和替换任意数量的括号字符串,而不是仅一个?首先,我认为仅正则表达式无法解决问题。JvdV证明这是错误的,做得好。老实说,我不明白这个正则表达式了 我用一些简单的正则表达式和一点python解决了这个问题
import re
intext = '[B] (foo) (bar) [/B] (not) [B] (this again) [/B]'
boldParts = re.findall(r'\[B\].*?\[/B\]', intext)
outtext = intext
for part in boldParts:
replacement = re.sub(r'(\(.*?\))', r'[C]\1[/C]', part)
outtext = outtext.replace(part, replacement)
print(outtext)
首先,我只查找intext中的粗体部分,然后很容易替换括号中的内容。并在outtext中再次替换它
诚然,这不是最短或最优雅的方法,但可能更具可读性。这种问题通常通过仅在其他匹配中替换匹配来解决。您需要使用一个正则表达式运行一个
re.sub
,该正则表达式将匹配所有B
标记的子字符串,并使用一个可调用的inre.sub
作为替换参数,仅替换这些匹配项中括号之间出现的多个字符串
以下是解决方案:
import re
text = "[B] blah blah (foo) blah [/B]\n[B] (foo) (bar) [/B]"
print(re.sub(r'(?s)\[B].*?\[/B]', lambda x: re.sub(r'\([^()]*\)', r'[C]\g<0>[/C]', x.group()), text))
看
输出:
[B] blah blah [C](foo)[/C] blah [/B]
[B] [C](foo)[/C] [C](bar)[/C] [/B]
(?s)\[B].\[/B]
模式匹配[B]
,然后0+字符尽可能少,直到最左边的[/B]
(注意(?s)
允许
匹配任何字符,包括换行字符)。然后,一旦找到匹配项,就将其传递给可调用对象,并在该匹配项上运行\([^()]*\)
正则表达式\([^()]*\)
匹配最近括号之间的任何子字符串,即(
),然后是(
和)
以外的0+字符,然后是)
。替换模式中的\g
是整个匹配的替换反向引用。好的。。这花了我一些时间。。我不确定标记语法的细节,但我将做一些假设:括号内的文本可以是除括号外的任何字符,除非它们被转义。转义字符是反斜杠。尽管如此。。这是我想到的
>>> expr = r"""
... \( # Match left paren.
... (
... (?: [^ \( \) \\] | # Match any char not a paren or escape, OR
... \\ [ \( \) \\] | # Match an escaped paren or escape, OR
... \s # whitespace.
... )*
... )
... \) # Match right paren. """
...
>>> re.sub(expr, r"[C](\1)[/C]", "[B] (foo) (bar) [/B]", flags=re.VERBOSE)
'[B] [C](foo)[/C] [C](bar)[/C] [/B]'
这也适用于包含转义括号的目标字符串。上面的缩写形式是
re.sub(r"\(((?:[^\(\)\\]|\\[\(\)\\]|\s)*)\)",
r"[C](\1)[/C]", "[B] (foo) (bar) [/B]")
去掉空格和注释后…处理此问题的基本思路是使用两个正则表达式,首先选择
B
标记内的值,然后捕获()
内的值并替换,如果括号之间的文本是自由形式的,并且可能包含其他转义括号,我认为你不能用一个正则表达式来完成。。除了简单的案例外,其他答案不会有太多的应用。如果我说的是真的,你可能想看看我的=)解。你建议我更详细地解释这个表达式吗?我需要先确认一下它是否正确=)。感谢你的回答+我很好奇你的正则表达式是如何工作的。我在你提供的链接中查到了。这有点帮助。然后我发现我可以想象它。然后它发出咔嗒声,我得到了它。对我来说,这种想象是关键。不知道(?!)反向查找仅简单[^]。所以,谢谢你,今天学到了一些东西。:)我会添加此链接作为对您答案的评论。但是我还不允许留下评论:((为您添加=)。顺便说一句,添加了\B
以防止测试(test)测试上的匹配。谢谢,我感到荣幸=)我相信[^()\]不匹配表达式中的空格的原因是使用re.VERBOSE表达式语法的副作用。这就是表达式中包含“\s”alt的原因。另外,请注意,如果括号的转义符类似于“('和')”,则可以轻松修改此表达式。我认为,使用re.VERBOSE标志=)可以让人们了解到,从这个表达式中可以获得更易于管理的表达式
re.sub(r"\(((?:[^\(\)\\]|\\[\(\)\\]|\s)*)\)",
r"[C](\1)[/C]", "[B] (foo) (bar) [/B]")