使用Python剥离和替换高Unicode字符文档的最快方法是什么?
我想在一个很大的文档中,把所有的高Unicode字符,比如带重音的E、左右引号等,替换成“正常”的低Unicode字符,比如普通的'E'和直引号。我需要经常在这个很大的文档上进行这样的操作。我看到一个可能是perl的例子在这里:http://www.designmeme.com/mtplugins/lowdown.txt
有没有什么快速的方法可以在Python中做到这一点,而不是用s.replace(...).replace(...).replace(...)这种方式?我尝试过替换几个字符,但文档处理起来变得非常慢。
编辑,我的版本是unutbu的代码,但似乎不太好用:
# -*- coding: iso-8859-15 -*-
import unidecode
def ascii_map():
data={}
for num in range(256):
h=num
filename='x{num:02x}'.format(num=num)
try:
mod = __import__('unidecode.'+filename,
fromlist=True)
except ImportError:
pass
else:
for l,val in enumerate(mod.data):
i=h<<8
i+=l
if i >= 0x80:
data[i]=unicode(val)
return data
if __name__=='__main__':
s = u'“fancy“fancy2'
print(s.translate(ascii_map()))
5 个回答
我觉得 unicodedata
对于花哨的引号是没用的。在这种情况下,你可以使用 Unidecode
:
import unidecode
print unidecode.unidecode(u"ß‘’“”")
# -> ss''""
其实并没有所谓的“高ASCII字符”。ASCII字符集的范围是从0到127。
说到这个,这个问题其实很常见。这里有一个答案。一般来说,你应该了解一下str.translate()和unicode.translate()这两个函数——它们在处理多个单字节/字符替换时非常有用。要小心那些只提到unicodedata.normalize()的回答;那只是解决方案的一部分。
更新:目前被接受的答案会删除那些没有分解的字符,正如Mark Tolonen所指出的。似乎大家对unicode.translate()
的功能了解得不够。它可以把一个字符转换成多个字符。以下是help(unicode.translate)
的输出:
S.translate(table) -> unicode
返回字符串S的一个副本,所有字符都通过给定的翻译表进行映射,这个翻译表必须是Unicode编码到Unicode编码、Unicode字符串或None的映射。未映射的字符保持不变。映射到None的字符会被删除。
这里有一个例子:
>>> u"Gau\xdf".translate({0xdf: u"ss"})
u'Gauss'
>>>
这是我提到的解决方案中的修正表:
CHAR_REPLACEMENT = {
# latin-1 characters that don't have a unicode decomposition
0xc6: u"AE", # LATIN CAPITAL LETTER AE
0xd0: u"D", # LATIN CAPITAL LETTER ETH
0xd8: u"OE", # LATIN CAPITAL LETTER O WITH STROKE
0xde: u"Th", # LATIN CAPITAL LETTER THORN
0xdf: u"ss", # LATIN SMALL LETTER SHARP S
0xe6: u"ae", # LATIN SMALL LETTER AE
0xf0: u"d", # LATIN SMALL LETTER ETH
0xf8: u"oe", # LATIN SMALL LETTER O WITH STROKE
0xfe: u"th", # LATIN SMALL LETTER THORN
}
这个方法可以很容易地扩展,以处理cp1252及其相关编码中出现的花哨引号和其他非latin-1字符。
# -*- encoding: utf-8 -*-
import unicodedata
def shoehorn_unicode_into_ascii(s):
return unicodedata.normalize('NFKD', s).encode('ascii','ignore')
if __name__=='__main__':
s = u"éèêàùçÇ"
print(shoehorn_unicode_into_ascii(s))
# eeeaucC
注意,正如@Mark Tolonen友好地指出的,上面的方法会去掉一些字符,比如ß‘’“”。如果上面的代码删掉了你想要翻译的字符,那么你可能需要用字符串的translate
方法来手动解决这些问题。另一个选择是使用unidecode(可以参考J.F. Sebastian的回答)。
当你处理一个很大的unicode字符串时,使用它的translate
方法会比使用replace
方法快得多。
编辑:unidecode
有一个更完整的unicode代码点到ascii的映射。不过,unidecode.unidecode
是逐个字符地遍历字符串(在Python循环中),这比使用translate
方法要慢。
以下这个辅助函数使用了unidecode
的数据文件,并结合translate
方法,以获得更好的速度,特别是对于长字符串。
在我对1-6 MB文本文件的测试中,使用ascii_map
的速度大约是unidecode.unidecode
的4-6倍。
# -*- coding: utf-8 -*-
import unidecode
def ascii_map():
data={}
for num in range(256):
h=num
filename='x{num:02x}'.format(num=num)
try:
mod = __import__('unidecode.'+filename,
fromlist=True)
except ImportError:
pass
else:
for l,val in enumerate(mod.data):
i=h<<8
i+=l
if i >= 0x80:
data[i]=unicode(val)
return data
if __name__=='__main__':
s = u"éèêàùçÇ"
print(s.translate(ascii_map()))
# eeeaucC
编辑2:Rhubarb,如果# -*- encoding: utf-8 -*-
导致了语法错误,试试# -*- encoding: cp1252 -*-
。声明什么编码取决于你的文本编辑器用什么编码来保存文件。Linux通常使用utf-8,而(似乎)Windows通常使用cp1252。