【问题标题】:latin-1 to asciilatin-1 到 ascii
【发布时间】:2009-09-05 10:44:40
【问题描述】:

我有一个带有重音拉丁字符的 unicode 字符串,例如

n=unicode('Wikipédia, le projet d’encyclopédie','utf-8')

我想将其转换为纯 ascii,即“Wikipedia, le projet dencyclopedie”,因此应删除所有尖锐/口音、cedilla 等

最快的方法是什么,因为它需要匹配一个长的自动完成下拉列表

结论: 由于我的标准之一是速度,Lennart 的“为 unicode 编码/解码注册您自己的错误处理程序”提供了最佳结果(请参阅 Alex 的回答),随着越来越多的字符是拉丁文,速度差异进一步增加。

这是我正在使用的转换表,还修改了错误处理程序,因为它需要处理从 error.start 到 error.end 的整个未编码字符范围

# -*- coding: utf-8 -*-
import codecs

"""
This is more of visual translation also avoiding multiple char translation
e.g. £ may be written as {pound}
"""
latin_dict = {
u"¡": u"!", u"¢": u"c", u"£": u"L", u"¤": u"o", u"¥": u"Y",
u"¦": u"|", u"§": u"S", u"¨": u"`", u"©": u"c", u"ª": u"a",
u"«": u"<<", u"¬": u"-", u"­": u"-", u"®": u"R", u"¯": u"-",
u"°": u"o", u"±": u"+-", u"²": u"2", u"³": u"3", u"´": u"'",
u"µ": u"u", u"¶": u"P", u"·": u".", u"¸": u",", u"¹": u"1",
u"º": u"o", u"»": u">>", u"¼": u"1/4", u"½": u"1/2", u"¾": u"3/4",
u"¿": u"?", u"À": u"A", u"Á": u"A", u"Â": u"A", u"Ã": u"A",
u"Ä": u"A", u"Å": u"A", u"Æ": u"Ae", u"Ç": u"C", u"È": u"E",
u"É": u"E", u"Ê": u"E", u"Ë": u"E", u"Ì": u"I", u"Í": u"I",
u"Î": u"I", u"Ï": u"I", u"Ð": u"D", u"Ñ": u"N", u"Ò": u"O",
u"Ó": u"O", u"Ô": u"O", u"Õ": u"O", u"Ö": u"O", u"×": u"*",
u"Ø": u"O", u"Ù": u"U", u"Ú": u"U", u"Û": u"U", u"Ü": u"U",
u"Ý": u"Y", u"Þ": u"p", u"ß": u"b", u"à": u"a", u"á": u"a",
u"â": u"a", u"ã": u"a", u"ä": u"a", u"å": u"a", u"æ": u"ae",
u"ç": u"c", u"è": u"e", u"é": u"e", u"ê": u"e", u"ë": u"e",
u"ì": u"i", u"í": u"i", u"î": u"i", u"ï": u"i", u"ð": u"d",
u"ñ": u"n", u"ò": u"o", u"ó": u"o", u"ô": u"o", u"õ": u"o",
u"ö": u"o", u"÷": u"/", u"ø": u"o", u"ù": u"u", u"ú": u"u",
u"û": u"u", u"ü": u"u", u"ý": u"y", u"þ": u"p", u"ÿ": u"y", 
u"’":u"'"}

def latin2ascii(error):
    """
    error is  protion of text from start to end, we just convert first
    hence return error.start+1 instead of error.end
    """
    return latin_dict[error.object[error.start]], error.start+1

codecs.register_error('latin2ascii', latin2ascii)

if __name__ == "__main__":
    x = u"¼ éíñ§ÐÌëÑ » ¼ ö ® © ’"
    print x
    print x.encode('ascii', 'latin2ascii')

我为什么返回error.start + 1

返回的错误对象可以是多个字符,我们只转换其中的第一个,例如如果我将print error.start, error.end 添加到错误处理程序输出是

¼ éíñ§ÐÌëÑ » ¼ ö ® © ’
0 1
2 10
3 10
4 10
5 10
6 10
7 10
8 10
9 10
11 12
13 14
15 16
17 18
19 20
21 22
1/4 einSDIeN >> 1/4 o R c '

所以在第二行我们得到 2-10 的字符,但我们只转换第二个因此返回 3 作为继续点,如果我们返回 error.end 输出是

¼ éíñ§ÐÌëÑ » ¼ ö ® © ’
0 1
2 10
11 12
13 14
15 16
17 18
19 20
21 22
1/4 e >> 1/4 o R c '

我们可以看到 2-10 部分已被单个字符替换。当然,一次性编码整个范围并返回 error.end 会更快,但出于演示目的,我保持简单。

查看http://docs.python.org/library/codecs.html#codecs.register_error了解更多详情

【问题讨论】:

  • 我相信您已经知道了,但请注意不要向用户显示这些 ascii-fied 字符串。当您或多或少地随意更改字母时(将“ö”变为“o”等等),单词的含义可能会完全改变。
  • 是的,这不是用于显示,而是用于打字,我们有一个带有 ascii 字母的屏幕键盘问题是用户如何键入 é 或 õ,所以如果键入 e,它应该匹配具有 e、é 的字符串, ê 等
  • 我不明白你用error.start+1 代替error.end。你能解释一下吗?两者对我来说似乎都一样。
  • @gorus 我已经添加了有问题的原因
  • 知道替换某些字符可以get somebody killed

标签: python unicode


【解决方案1】:

所以这里有三种方法,或多或少在其他答案中给出或建议:

# -*- coding: utf-8 -*-
import codecs
import unicodedata

x = u"Wikipédia, le projet d’encyclopédie"

xtd = {ord(u'’'): u"'", ord(u'é'): u'e', }

def asciify(error):
    return xtd[ord(error.object[error.start])], error.end

codecs.register_error('asciify', asciify)

def ae():
  return x.encode('ascii', 'asciify')

def ud():
  return unicodedata.normalize('NFKD', x).encode('ASCII', 'ignore')

def tr():
  return x.translate(xtd)

if __name__ == '__main__':
  print 'or:', x
  print 'ae:', ae()
  print 'ud:', ud()
  print 'tr:', tr()

作为 main 运行,这会发出:

or: Wikipédia, le projet d’encyclopédie
ae: Wikipedia, le projet d'encyclopedie
ud: Wikipedia, le projet dencyclopedie
tr: Wikipedia, le projet d'encyclopedie

清楚地表明,基于 unicodedata 的方法虽然确实具有不需要翻译映射 xtd 的便利性,但不能以自动方式正确翻译所有字符(它适用于重音字母,但不适用于反向-撇号),因此它还需要一些辅助步骤来明确处理这些(毫无疑问,在它现在的主体之前)。

性能也很有趣。在我的装有 Mac OS X 10.5 和系统 Python 2.5 的笔记本电脑上,非常可重复:

$ python -mtimeit -s'import a' 'a.ae()'
100000 loops, best of 3: 7.5 usec per loop
$ python -mtimeit -s'import a' 'a.ud()'
100000 loops, best of 3: 3.66 usec per loop
$ python -mtimeit -s'import a' 'a.tr()'
10000 loops, best of 3: 21.4 usec per loop

translate 出奇地慢(相对于其他方法)。我认为问题在于,对于 translate 案例中的每个字符(并且大多数不存在),都会对字典进行调查,但仅针对 asciify 方法中存在的少数字符。

所以为了完整起见,这里是“加强的 unicodedata”方法:

specstd = {ord(u'’'): u"'", }
def specials(error):
  return specstd.get(ord(error.object[error.start]), u''), error.end
codecs.register_error('specials', specials)

def bu():
  return unicodedata.normalize('NFKD', x).encode('ASCII', 'specials')

这给出了正确的输出,但是:

$ python -mtimeit -s'import a' 'a.bu()'
100000 loops, best of 3: 10.7 usec per loop

...速度不再那么好。因此,如果速度很重要,毫无疑问,制作完整的xtd 翻译字典并使用asciify 方法是值得的。当每次翻译多出几微秒没什么大不了的时候,人们可能只想考虑bu 方法,只是为了方便(只需要一个翻译字典,希望很少,特殊字符不能用底层的 unicodedata 思想正确翻译)。

【讨论】:

  • 感谢您的总结和时间安排 :)
  • 在创建 dict 时执行 'ord' 并在 asciifying 时再次获得序数是否有原因?
  • @Anurag,dict 采用这种方式的原因是为了让它在.translate 中立即可用——asciify 当然不需要。简化将其时间大致从 7.5 微秒减少到 7.3 微秒。
【解决方案2】:

了不起的unidecode 模块为您完成了这项工作:

>>> import unidecode
>>> n = unicode('Wikipédia, le projet d’encyclopédie','utf-8')
>>> unidecode.unidecode(n)
"Wikipedia, le projet d'encyclopedie"

【讨论】:

    【解决方案3】:

    “正确”的做法是为 unicode 编码/解码注册您自己的错误处理程序,并在该错误处理程序中提供从 è 到 e 和 ö 到 o 等的替换。

    像这样:

    # -*- coding: UTF-8 -*-
    import codecs
    
    map = {u'é': u'e',
           u'’': u"'",
           # ETC
           }
    
    def asciify(error):
        return map[error.object[error.start]], error.end
    
    codecs.register_error('asciify', asciify)
    
    test = u'Wikipédia, le projet d’encyclopédie'
    print test.encode('ascii', 'asciify')
    

    您还可以在 IBM 的 ICU 库中找到一些东西,它是 Python 绑定 PyICU,不过,它的工作量可能会更少。

    【讨论】:

    • +1:我只想对 asciify 函数的输入添加更多检查,但我认为这对于 unicode 编码中的自定义错误处理也是一个非常快速且很好的参考。
    • 我同意这是正确的实现。也许有人可以建议一个完整的通用映射。
    • +1 表示正确答案,但我想我会选择亚历克斯的答案是完整的,其中包括时间。
    • asciify 也应该将所有字符从 error.start 转换为 error.end
    【解决方案4】:

    Maketrans(并翻译)然后转换为 ascii:

    intab = u'áéí'  # extend as needed
    outtab = u'aei' # as well as this one
    table = maketrans(intab, outtab)
    
    text = translate(u"Wikipédia, le projet d’encyclopédie", table)
    
    try:
        temp = unicode(text, "utf-8")
        fixed = unicodedata.normalize('NFKD', temp).encode('ASCII', action)
        return fixed
    except Exception, errorInfo:
        print errorInfo
        print "Unable to convert the Unicode characters to xml character entities"
        raise errorInfo
    

    (来自here

    【讨论】:

    • 但这会将它们转换为 xml 字符实体。这不是他要求的。
    • 我不明白解决方案的第一行“Maketrans (and translate) then convert to ascii:” 为什么需要它并且你不在代码中的任何地方使用它?
    • @Lennart Regebro:然后将它们编码为 ASCII。 @Anurag Uniyal:他想替换例如'é' 和 'e' 简单的转换不会为他做。这就是需要 maketrans 的原因。我这里复制的代码 sn -p 只显示了 unicode->ASCII 转换。
    • 我添加了 maketrans 示例。
    • @sztomi:这里的事情是他希望将非 ascii 字符转换为 ascii 字符,而您的示例没有这样做。您的 maketrans 示例甚至不使用 unicode...
    【解决方案5】:

    如果不进行测量,我希望 Unicode 字符串的 .translate 方法是最快的解决方案。不过,您绝对应该自己测量。

    【讨论】:

      【解决方案6】:

      unihandecode

      Unicode 文本的 US-ASCII 音译。
      Python unidecode 的改进版本,即 Sean M. Burke 的 Text::Unidecode Perl 模块的 Python 端口。

      pip install Unihandecode
      

      然后在python

      import unihandecode
      print(unihandecode.unidecode(u'Wikipédia, le projet d’encyclopédie'))
      

      打印Wikipedia, le projet d'encyclopedie

      【讨论】:

        猜你喜欢
        • 2012-02-11
        • 1970-01-01
        • 2011-05-06
        • 2017-09-28
        • 1970-01-01
        • 1970-01-01
        • 2013-06-11
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多