5

我正在寻找从大型文档中替换所有高 unicode 字符,例如重音 Es、左右引号等,以及低范围内的“正常”对应物,例如常规的“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()))
4

5 回答 5

8
# -*- 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(参见JF Sebastian 的回答)。

当你有一个大的 unicode 字符串时,使用它的translate方法会比使用方法快得多replace

编辑: unidecode具有更完整的 unicode 代码点到 ascii 的映射。但是,unidecode.unidecode逐个字符地循环字符串(在 Python 循环中),这比使用该translate方法要慢。

以下辅助函数使用unidecode's 数据文件,以及translate获得更好速度的方法,尤其是对于长字符串。

在我对 1-6 MB 文本文件的测试中,使用ascii_map速度比unidecode.unidecode.

# -*- 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

Edit2:Rhubarb,如果# -*- encoding: utf-8 -*-导致 SyntaxError,请尝试 # -*- encoding: cp1252 -*-. 声明什么编码取决于您的文本编辑器使用什么编码来保存文件。Linux 倾向于使用 utf-8,并且(似乎)Windows 倾向于使用 cp1252。

于 2010-05-18T02:39:36.570 回答
4

没有“高ASCII字符”这样的东西。ASCII 字符集仅限于范围 (128) 中的序数。

除此之外,这是一个常见问题解答。这是一个答案。一般来说,您应该熟悉 str.translate() 和 unicode.translate() - 对于单个字节/字符的多次替换非常方便。注意只提到 unicodedata.normalize() 噱头的答案;这只是解决方案的一部分。

更新:正如 Mark Tolonen 所指出的,当前接受的答案会吹走没有分解的角色。似乎缺乏对unicode.translate()能力的了解。它可以将一个字符翻译成多个字符。这是来自的输出help(unicode.translate)

S.translate(table) -> unicode

返回字符串 S 的副本,其中所有字符都已通过给定的转换表映射,该转换表必须是 Unicode 序数到 Unicode 序数、Unicode 字符串或无的映射。未映射的字符保持不变。映射到 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 字符。

于 2010-05-18T02:39:57.007 回答
3

我相信这unicodedata不适用于花哨的报价。您可以Unidecode在这种情况下使用:

import unidecode
print unidecode.unidecode(u"ß‘’“”")
# -> ss''""
于 2010-05-20T19:02:22.747 回答
1

如果建议的unicodedata.normalize()~unubtu不能解决问题,例如,如果您想更好地控制映射,您应该查看
str.translate()
str.maketrans(),这是一个生成地图的实用程序table, str.translate 对于这种类型的翻译既高效又方便。
在 Python 2.x 和 unicode 字符串中,需要使用unicode.translate()而不是 str.translate() 和类似于下面代码片段中所示的技巧,以代替 maketrans()。(感谢 John Machin 指出这一点!)

这些方法在 Python 3.x 中也可用,请参见Python 3.1.2 文档 (出于某种原因,我已经注意到这在 Python 3.x 中可能已经改变)。当然,在 Python 3 下,所有字符串都是 unicode 字符串,但这是另一个问题。

#Python 3.1
>>> intab = 'àâçêèéïîôù'
>>> outtab = 'aaceeeiiou'
>>> tmap = str.maketrans(intab, outtab)
>>> s = "à la fête de l'été, où il fait bon danser, les Français font les drôles"
>>> s
"à la fête de l'été, où il fait bon danser, les Français font les drôles"
>>> s.translate(tmap)
"a la fete de l'ete, ou il fait bon danser, les Francais font les droles"
>>>


#Python 2.6
>>> intab = u'àâçêèéïîôù'
>>> outtab = u'aaceeeiiou'
>>> s = u"à la fête de l'été, où il fait bon danser, les Français font les drôles"
>>> #note the trick to replace maketrans() since for unicode strings the translation
>>> #     map expects integers (unicode ordinals) not characters.
>>> tmap = dict(zip(map(ord, intab), map(ord, outtab))) 
>>> s.translate(tmap)
u"a la fete de l'ete, ou il fait bon danser, les Francais font les droles"
>>>
于 2010-05-18T02:40:06.333 回答
0

这是处理 latin-1 字符的解决方案(基于 2003 usenet 线程):

>>> accentstable = str.join("", map(chr, range(192))) + "AAAAAAACEEEEIIIIDNOOOOOxOUUUUYTsaaaaaaaceeeeiiiidnooooo/ouuuuyty"
>>> import string
>>> s = u"éèêàùçÇ"
>>> print string.translate(s.encode('latin1', 'ignore'), accentstable)
eeeaucC

有些映射并不完美,例如 Thorn 映射到 T 而不是 Th,但它的工作还算可以接受。

于 2010-05-18T07:49:12.683 回答