另一种比上述答案快得多的方法是使用正则表达式,如下所示:
re.sub(u'[^\u0020-\uD7FF\u0009\u000A\u000D\uE000-\uFFFD\U00010000-\U0010FFFF]+', '', text)
与上面的答案相比,在我的测试中它的速度提高了 10 倍以上:
import timeit
func_test = """
def valid_xml_char_ordinal(c):
codepoint = ord(c)
# conditions ordered by presumed frequency
return (
0x20 <= codepoint <= 0xD7FF or
codepoint in (0x9, 0xA, 0xD) or
0xE000 <= codepoint <= 0xFFFD or
0x10000 <= codepoint <= 0x10FFFF
);
''.join(c for c in r.content if valid_xml_char_ordinal(c))
"""
func_setup = """
import requests;
r = requests.get("https://stackoverflow.com/questions/8733233/")
"""
regex_test = """re.sub(u'[^\u0020-\uD7FF\u0009\u000A\u000D\uE000-\uFFFD\U00010000-\U0010FFFF]+', '', r.content)"""
regex_setup = """
import requests, re;
r = requests.get("https://stackoverflow.com/questions/8733233/")
"""
func_test = timeit.Timer(func_test, setup=func_setup)
regex_test = timeit.Timer(regex_test, setup=regex_setup)
print func_test.timeit(100)
print regex_test.timeit(100)
输出:
> 2.63773989677
> 0.221401929855
因此,理解这一点,我们正在做的是下载该网页一次(您当前正在阅读的页面),然后在其内容上运行函数技术和正则表达式技术各 100 倍。
使用函数式方法大约需要 2.6 秒。
使用正则表达式方法大约需要 0.2 秒。
更新:如 cmets 中所述,此答案中的正则表达式之前删除了一些字符,这些字符在 XML 中应该是允许的。这些字符包括 Supplementary Multilingual Plane 中的任何内容,其中包括古代文字,如楔形文字、象形文字和(奇怪的)表情符号。
正确的正则表达式现在在上面。将来对此的快速测试是使用re.DEBUG,它会打印:
In [52]: re.compile(u'[^\u0020-\uD7FF\u0009\u000A\u000D\uE000-\uFFFD\U00010000-\U0010FFFF]+', re.DEBUG)
max_repeat 1 4294967295
in
negate None
range (32, 55295)
literal 9
literal 10
literal 13
range (57344, 65533)
range (65536, 1114111)
Out[52]: re.compile(ur'[^ -\ud7ff\t\n\r\ue000-\ufffd\U00010000-\U0010ffff]+', re.DEBUG)
我为这个错误道歉。我只能提供我在其他地方找到了这个答案并把它放在这里。这是别人的错误,但我传播了它。我向所有受到影响的人表示诚挚的歉意。
2017 年 12 月 12 日第 2 次更新:我从一些 OSX 用户那里了解到,此代码不适用于所谓的 Python 窄版本,而 OSX 有时会这样做。您可以通过运行import sys; sys.maxunicode 来检查这一点。如果它打印 65535,则在您安装“宽版本”之前,此处的代码将不起作用。 See more about this here.