部分替换/删除正则表达式匹配中的字符

Partial substitution / removing characters within regex matches

如果字符 \/<>~?`% 出现在三个 &lt;&gt; 中,我将尝试删除它们。

对于字符串:

<html><body>Multiple &lt;&lt;&lt;parameter&gt;&gt;&gt; options %to &lt;&lt;&lt;test/verify&gt;&gt;&gt; in &lt;&lt;&lt;one% g?o&gt;&gt;&gt;</body></html>

(读起来像 Multiple <<<parameter>>> options %to <<<test/verify>>> in <<<one% g?o>>>。)

我想要的最终字符串是:

<html><body>Multiple &lt;&lt;&lt;parameter&gt;&gt;&gt; options %to &lt;&lt;&lt;testverify&gt;&gt;&gt; in &lt;&lt;&lt;one go&gt;&gt;&gt;</body></html>

请注意,“%to”中的“%”未被删除,因为它不在三个 &lt;&gt; 之内。

到目前为止,我尝试了这些正则表达式:

>>> s = '<html><body>Multiple &lt;&lt;&lt;parameter&gt;&gt;&gt; options %to &lt;&lt;&lt;test/verify&gt;&gt;&gt; in &lt;&lt;&lt;one% g?o&gt;&gt;&gt;</body></html>'
>>>
>>> # just getting everything between <<< and >>> is easy
... re.sub(r'((?:&lt;){3})(.*?)((?:&gt;){3})', r'', s)
'<html><body>Multiple &lt;&lt;&lt;parameter&gt;&gt;&gt; options %to &lt;&lt;&lt;test/verify&gt;&gt;&gt; in &lt;&lt;&lt;one%? go&gt;&gt;&gt;</body></html>'
>>> re.findall(r'((?:&lt;){3})(.*?)((?:&gt;){3})', s)
[('&lt;&lt;&lt;', 'parameter', '&gt;&gt;&gt;'),
 ('&lt;&lt;&lt;', 'test/verify', '&gt;&gt;&gt;'),
 ('&lt;&lt;&lt;', 'one%? go', '&gt;&gt;&gt;')]

但是试图获取一系列非\/<>~?`%字符doesn't work因为任何包含它的东西都会被排除在外:

>>> re.findall(r'((?:&lt;){3})([^\/<>~?`%]*?)((?:&gt;){3})', s)
[('&lt;&lt;&lt;', 'parameter', '&gt;&gt;&gt;')]
>>> re.findall(r'((?:&lt;){3})((?:[^\/<>~?`%]*?)*?)((?:&gt;){3})', s)
[('&lt;&lt;&lt;', 'parameter', '&gt;&gt;&gt;')]
>>> re.findall(r'((?:&lt;){3})((?:[^\/<>~?`%])*?)((?:&gt;){3})', s)
[('&lt;&lt;&lt;', 'parameter', '&gt;&gt;&gt;')]

我采用的解决方案是使用原始的 <<<.*>>> 正则表达式和 re.subrepl as a function 选项:

>>> def illrepl(matchobj):
...     return ''.join([matchobj.group(1),
...                     matchobj.group(2).translate(None, r'\/<>~?`%'),
...                     matchobj.group(3)])
...
>>> re.sub(r'((?:&lt;){3})(.*?)((?:&gt;){3})', illrepl, s)
'<html><body>Multiple &lt;&lt;&lt;parameter&gt;&gt;&gt; options %to &lt;&lt;&lt;testverify&gt;&gt;&gt; in &lt;&lt;&lt;one go&gt;&gt;&gt;</body></html>'
>>> # verify that this is the final string I wanted:
... re.sub(r'((?:&lt;){3})(.*?)((?:&gt;){3})', illrepl, s) == '<html><body>Multiple &lt;&lt;&lt;parameter&gt;&gt;&gt; options %to &lt;&lt;&lt;testverify&gt;&gt;&gt; in &lt;&lt;&lt;one go&gt;&gt;&gt;</body></html>'
True

并且由于我不需要更改 &lt;&gt;,而且我知道匹配仅适用于其中的内容,所以我可以使用非捕获对正则表达式的那些部分进行分组或通过仅使用 group(0) 处的完整匹配对象来删除 illegal/invalid 个字符来稍微简化 illrepl 函数:

>>> def illrepl(matchobj):
...     # return matchobj.group(0).translate(None, r'\/<>~?`%')  # may have unicode so can't use this
...     return re.sub(r'[\/<>~?`%]*', '', matchobj.group(0))
...
>>> re.sub(r'(?:&lt;){3}(.*?)(?:&gt;){3}', illrepl, s)
'<html><body>Multiple &lt;&lt;&lt;parameter&gt;&gt;&gt; options %to &lt;&lt;&lt;testverify&gt;&gt;&gt; in &lt;&lt;&lt;one go&gt;&gt;&gt;</body></html>'

不确定是否有办法只通过正则表达式完成此操作,而不需要使用 illrepl 函数来生成替换并且必须在其中再次使用 re.sub