如何让 str.translate 使用 Unicode 字符串?

Posted

技术标签:

【中文标题】如何让 str.translate 使用 Unicode 字符串?【英文标题】:How do I get str.translate to work with Unicode strings? 【发布时间】:2010-11-22 09:12:30 【问题描述】:

我有以下代码:

import string
def translate_non_alphanumerics(to_translate, translate_to='_'):
    not_letters_or_digits = u'!"#%\'()*+,-./:;<=>?@[\]^_`|~'
    translate_table = string.maketrans(not_letters_or_digits,
                                       translate_to
                                         *len(not_letters_or_digits))
    return to_translate.translate(translate_table)

这对非 unicode 字符串非常有效:

>>> translate_non_alphanumerics('<foo>!')
'_foo__'

但 unicode 字符串失败:

>>> translate_non_alphanumerics(u'<foo>!')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in translate_non_alphanumerics
TypeError: character mapping must return integer, None or unicode

对于 str.translate() 方法,我无法理解 Python 2.6.2 docs 中关于“Unicode 对象”的段落。

我如何使这项工作适用于 Unicode 字符串?

【问题讨论】:

这是example that removes Unicode punctuation from a string using unicode.translate() method。 在实际代码中最好使用import string; string.punctuation 而不是硬编码not_letters_or_digits。我明白了,你宁愿明确一点。 【参考方案1】:

Unicode 版本的 translate 需要从 Unicode 序数(您可以使用 ord 检索单个字符)到 Unicode 序数的映射。如果你想删除字符,你映射到None

我改变了你的函数来构建一个字典,将每个字符的序数映射到你想要翻译的序数:

def translate_non_alphanumerics(to_translate, translate_to=u'_'):
    not_letters_or_digits = u'!"#%\'()*+,-./:;<=>?@[\]^_`|~'
    translate_table = dict((ord(char), translate_to) for char in not_letters_or_digits)
    return to_translate.translate(translate_table)

>>> translate_non_alphanumerics(u'<foo>!')
u'_foo__'

编辑: 事实证明,翻译映射必须从 Unicode 序数(通过ord)映射到另一个 Unicode 序数、Unicode 字符串或 None(要删除)。因此,我将translate_to 的默认值更改为Unicode 文字。例如:

>>> translate_non_alphanumerics(u'<foo>!', u'bad')
u'badfoobadbad'

【讨论】:

谢谢! (这样一个愚蠢的设计决定就是拥有一个名称相同但运行方式不同的函数。) 另外,如果你不想手动定义标点字符:import string; translate_table = ord(unicode(c)) for c in string.punctuation 注意:这不会翻译所有特殊的 unicode 标点字符(有很多...) 您的not_letters_or_digits 缺少“$”和“&”。让我建议使用 string.punctuation 而不是硬编码集合或字符【参考方案2】:

在这个版本中你可以相对地给别人写信

def trans(to_translate):
    tabin = u'привет'
    tabout = u'тевирп'
    tabin = [ord(char) for char in tabin]
    translate_table = dict(zip(tabin, tabout))
    return to_translate.translate(translate_table)

【讨论】:

【参考方案3】:

我想出了以下组合我的原始函数和Mike 的版本,适用于 Unicode 和 ASCII 字符串:

def translate_non_alphanumerics(to_translate, translate_to=u'_'):
    not_letters_or_digits = u'!"#%\'()*+,-./:;<=>?@[\]^_`|~'
    if isinstance(to_translate, unicode):
        translate_table = dict((ord(char), unicode(translate_to))
                               for char in not_letters_or_digits)
    else:
        assert isinstance(to_translate, str)
        translate_table = string.maketrans(not_letters_or_digits,
                                           translate_to
                                              *len(not_letters_or_digits))
    return to_translate.translate(translate_table)

更新:“强制”translate_to 为 unicode 为 unicode translate_table。谢谢迈克。

【讨论】:

我建议您将 Unicode 版本的 translate_to 强制转换为 Unicode,否则如果您将 Unicode 字符串和“普通”字符串传递给 translate 调用,它就会崩溃。 这似乎应该是语言的一部分。 +1【参考方案4】:

对于一个适用于 str 和 unicode 对象的简单 hack, 在运行 translate() 之前将转换表转换为 unicode:

import string
def translate_non_alphanumerics(to_translate, translate_to='_'):
    not_letters_or_digits = u'!"#%\'()*+,-./:;<=>?@[\]^_`|~'
    translate_table = string.maketrans(not_letters_or_digits,
                                       translate_to
                                         *len(not_letters_or_digits))
    translate_table = translate_table.decode("latin-1")
    return to_translate.translate(translate_table)

这里的问题是它会将所有 str 对象隐式转换为 unicode, 如果 to_translate 包含非 ascii 字符,则会引发错误。

【讨论】:

【参考方案5】:

不必指定所有需要替换的字符,您也可以反过来查看,只指定有效字符,如下所示:

import re

def replace_non_alphanumerics(source, replacement_character='_'):
    result = re.sub("[^_a-zA-Z0-9]", replacement_character, source)

    return result

这适用于 unicode 和常规字符串,并保留类型(如果 replacement_charactersource 显然属于同一类型)。

【讨论】:

【参考方案6】:

我发现在 python 2.7 中,类型为str,你会写

import string
table = string.maketrans("123", "abc")
print "135".translate(table)

而你会说 unicode 类型

table = ord(s): unicode(d) for s, d in zip("123", "abc")
print u"135".translate(table)

在 python 3.6 中你会写

table = ord(s): d for s, d in zip("123", "abc")
print("135".translate(table))

也许这会有所帮助。

【讨论】:

【参考方案7】:

与这里的其他人相比,我遇到了一个独特的问题。首先,我知道我的字符串中可能包含 unicode 字符。 (感谢 Mac 上的电子邮件...)但常见的字符之一是 emdash AKA u"\u2014" 字符,需要将其转换(返回)为两个破折号 AKA“--”。可能找到的其他字符是单字符翻译,因此它们与其他解决方案类似。

首先我为 emdash 创建了一个字典。对于这些,我使用一个简单的 string.replace() 来转换它们。其他类似的字符也可以在这里处理。

uTranslateDict = 
    u"\u2014": "--", # Emdash

然后我为 1:1 翻译创建了一个元组。这些通过 string.translate() 内置。

uTranslateTuple = [(u"\u2010", "-"),  # Hyphen
                   (u"\u2013", "-"),  # Endash
                   (u"\u2018", "'"),  # Left single quote => single quote
                   (u"\u2019", "'"),  # Right single quote => single quote
                   (u"\u201a", "'"),  # Single Low-9 quote => single quote
                   (u"\u201b", "'"),  # Single High-Reversed-9 quote => single quote
                   (u"\u201c", '"'),  # Left double quote => double quote
                   (u"\u201d", '"'),  # Right double quote => double quote
                   (u"\u201e", '"'),  # Double Low-9 quote => double quote
                   (u"\u201f", '"'),  # Double High-Reversed-9 quote => double quote
                   (u"\u2022", "*"),  # Bullet
]

然后是函数。

def uTranslate(uToTranslate):
    uTranslateTable = dict((ord(From), unicode(To)) for From, To in uTranslateTuple)
    for c in uTranslateDict.keys():
        uIntermediateStr = uToTranslate.decode("utf-8").replace(c, uTranslateDict[c])
    return uIntermediateStr.translate(uTranslateTable)

由于我知道输入字符串的格式,我不必担心两种类型的输入字符串。

【讨论】:

以上是关于如何让 str.translate 使用 Unicode 字符串?的主要内容,如果未能解决你的问题,请参考以下文章

Python3基础 str translate 将指定字符转换成另一种特定字符

Python学习:maketrans和translate方法

Python translate()方法

镜像字符串

python内置string模块

在python中打印下标