Python中的正则表达式排除匹配

3

我对正则表达式不是很熟悉,所以我正在寻找排除某些内容的语法。 我正在解析html代码中的<>"&(以替换为&lt;等),并且我需要排除解析<br/>。 例如:

<html><br/>
   <head><title></title></head><br/>
   <body><br/>
   </body><br/>
</html>

我尝试了一些类似于r'<\b?![br]'等的方法,但它们并不完全有效。我使用re.sub()进行替换。

2
@ stdio,你不需要外部库;Python自带优秀的ElementTree(一个API,lxml提供了更好的实现),可以直接使用。 - Charles Duffy
1
XML(类似于扩展SGML)不是常规语言(在计算机科学术语的意义上——如果您参加过编译器设计课程,他们应该会涉及它)。正则表达式不足以解析它。 - Charles Duffy
2
@Charles 大多数现代正则表达式实现(包括Python的)并不是真正的正则表达式。此外,关闭这个答案作为那个笑话帖子的重复,对OP没有任何帮助。 - NullUserException
4
这个问题被错误地标记为一个完全重复的答案,而那只是一个笑话!你还能更愚蠢和无聊 - 错误得多么离谱?我投票支持重新开放。这个人应该得到对他问题的回答。在这里像“烧女巫”的态度太过分了! - tchrist
2
除非我漏掉了什么,一旦它只是<br/>(没有任何变体),那么只需用&lt;替换<(?!br/>),用&gt;替换(?<!<br/)>,就可以了吗? - Peter Boughton
显示剩余10条评论
3个回答

3

好的,现在问题再次开放,我可以将其作为答案来完成,所以......

除非我漏掉了什么,一旦只有<br/>(没有任何变体),那么只需用&lt;替换<(?!br/>),并用&gt;替换(?<!<br/)>,就可以了吗?


在Python中,看起来是这样的:

text = re.sub( '<(?!br/>)' , '&lt;' , text )
text = re.sub( '(?<!<br/)>' , '&gt;' , text )

为了解释正在发生的事情,(?!...)是一个负向先行断言——只有在其后面的文本不匹配其子表达式时,它才能成功匹配。(请注意,先行断言不会消耗其子表达式匹配的文本,它们只验证是否存在或不存在。) 类似地,(?<!...)是一个负向后行断言,使用前面的文本来做同样的事情。
然而,在某些正则表达式实现中,后行断言与先行断言略有不同——即后行断言中的子表达式必须表示固定宽度或有限宽度的匹配。
Python是其中一个需要固定宽度的实现——因此,尽管上述表达式有效(因为它始终是四个字符),但如果它是(?<!<br\s*/?)>,那么它将不是Python的有效正则表达式,因为它代表着可变长度的匹配。(但是,您可以堆叠多个后行断言,因此如果有必要,您可以手动迭代各种选项。)

我已经说过了:完美 ;) 现在,有没有一种方法可以一步完成所有操作?对于正则表达式没有问题,我可以使用“或”运算符(|),但是是否有办法将多个值作为第二个参数传递给re.sub()函数? - stdio
1
你正在用不同的东西进行替换,所以不能在一步中完成。嗯,我认为PHP允许您传递一个数组(用于正则表达式和替换),但是Python文档中没有提到这一点,因此如果它很重要,就需要一个用户定义的函数。当然,如果只是不想要一个临时变量,您也可以使用 re.sub('<(?!br/>)', '&lt;', re.sub('(?<!<br/)>', '&gt;', text)) - Peter Boughton

0

这符合您的需求吗?:

import re
import htmlentitydefs

ss = '''
<html>
    <br>
        <title>"War & Peace"</title>
        <body>Leon Tolstoy</body>
    <br/>
</html>'''

print ss
print '\n\n'


uniquechars_repl = '"&'
conditional_repl = {'<':'<(?!br/>)',
                    '>':'(?<!<br/)>'}

all_repl = list(uniquechars_repl) + conditional_repl.keys()

di = dict( (b,'&%s;' % a) for a,b in htmlentitydefs.entitydefs.iteritems()
           if b in all_repl)

pat = '|'.join(list(uniquechars_repl) + conditional_repl.values())

text = re.sub(pat , lambda mat: di[mat.group()], ss )

print text

结果

<html>
    <br>
        <title>"War & Peace"</title>
        <body>Leon Tolstoy</body>
    <br/>
</html>




&lt;html&gt;
    &lt;br&gt;
        &lt;title&gt;&quot;War &amp; Peace&quot;&lt;/title&gt;
        &lt;body&gt;Leon Tolstoy&lt;/body&gt;
    <br/>
&lt;/html&gt;

0

首先替换所有内容,然后在第二次替换中将"&lt;br/&gt;"替换为"<br/>"。

或者,为了通用化,您可以列出要“还原”的标签列表,并将"&lt;tag&gt;"替换为"<tag>","&lt;/tag&gt;"替换为"</tag>","&lt;tag/&gt;"替换为"<tag/>"。


1
有更好更优雅的方式吗?不过我更喜欢使用正则表达式。 - stdio
@stdio:但是这个答案确实使用了正则表达式。一旦你转换了所有内容,只需撤消你不想真正更改的标记即可。 - tchrist
@tchrist:是的,但不够优雅,我更喜欢使用re.sub()将所有内容一步完成(通过正则表达式排除“br”标记解析)。 - stdio
@ stdio:请编辑您的答案并显示当前正在执行的操作,以便我可以看到在哪里进行修改。你为什么要这样做?一些需要将所有 HTML 清理干净的 BB 帖子吗? - tchrist
@Joaquim Rendeiro:我的HTML代码与它类似,没有过早插入<br/>。 - stdio
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接