python 2和3中的UTF-8字符串

Mey*_*sam 5 python string utf-8 python-2.7 python-3.x

以下代码适用于Python 3:

people = [u'Nicholas Gyeney', u'Andr\xe9']
writers = ", ".join(people)
print(writers)
print("Writers: {}".format(writers))
Run Code Online (Sandbox Code Playgroud)

并产生以下输出:

Nicholas Gyeney, André  
Writers: Nicholas Gyeney, André
Run Code Online (Sandbox Code Playgroud)

但是,在Python 2.7中,我收到以下错误:

Traceback (most recent call last):
  File "python", line 4, in <module>
UnicodeEncodeError: 'ascii' codec can't encode character u'\xe9' 
in position 21: ordinal not in range(128)
Run Code Online (Sandbox Code Playgroud)

我可以通过更改", ".join(people)为修复此错误", ".join(people).encode('utf-8'),但如果我这样做,Python 3中的输出将更改为:

b'Nicholas Gyeney, Andr\xc3\xa9'  
Writers: b'Nicholas Gyeney, Andr\xc3\xa9'
Run Code Online (Sandbox Code Playgroud)

所以我尝试使用以下代码:

if sys.version_info < (3, 0):
    reload(sys)
    sys.setdefaultencoding('utf-8')

people = [u'Nicholas Gyeney', u'Andr\xe9']
writers = ", ".join(people)
print(writers)
print("Writers: {}".format(writers))
Run Code Online (Sandbox Code Playgroud)

这使我的代码适用于所有版本的Python.但我读到使用setdefaultencoding 是不鼓励的.

处理这个问题的最佳方法是什么?

qua*_*oft 8

首先,我们假设您要支持Python 2.7和3.5版本(2.6和3.0到3.2的处理方式略有不同).

正如您已经阅读过的那样,setdefaultencoding在您的情况下不鼓励并且实际上不需要.

要编写处理unicode文本的跨平台代码,通常只需要在几个地方指定字符串编码:

  1. 在脚本的顶部,在shebang下面# -*- coding: utf-8 -*-(只有你的代码中有unicode文本的字符串文字)
  2. 当您读取输入数据时(例如,来自文本文件或数据库)
  3. 输出数据时(再次从文本文件或数据库)
  4. 在代码中定义字符串文字时

以下是我按照这些规则更改示例的方法:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

people = ['Nicholas Gyeney', 'André']
writers = ", ".join(people)
print(writers)
print("Writers: {}".format(writers))

print(type(writers))
print(len(writers))
Run Code Online (Sandbox Code Playgroud)

哪个输出:

<type 'str'>
23
Run Code Online (Sandbox Code Playgroud)

这是改变了:

  • 在文件顶部指定文件编码
  • 替换\xe9为实际的Unicode字符(é)
  • 删除了u前缀

它在Python 2.7.12和3.5.2中运行得很好.

但请注意,删除u前缀将使python使用常规str类型而不是unicode(参见输出print(type(writers))).如果utf-8它在大多数地方工作,就好像它是一个unicode字符串,但在检查文本长度时,将返回错误的值.在此示例中len返回23实际的字符数22.这是因为底层类型是str将每个字节计为一个字符,但字符é实际上应该是两个字节.

换句话说,这在输出数据时很有效(如在您的示例中),但如果您想对文本执行字符串操作则不行.在这种情况下,您仍然需要使用u前缀或在字符串操作之前将数据明确地转换为unicode类型.

因此,如果不是您的简单示例,那么仍然使用u前缀会更好.你需要在两个地方:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

people = [u'Nicholas Gyeney', u'André']
writers = ", ".join(people)
print(writers)
print(u"Writers: {}".format(writers))

print(type(writers))
print(len(writers))
Run Code Online (Sandbox Code Playgroud)

哪个输出:

<type 'unicode'>
22
Run Code Online (Sandbox Code Playgroud)

注意:u在Python 3.0中删除了前缀,然后在Python 3.3中再次重新引入,以实现向后兼容.

有关在Python 2中使用unicode文本的所有复杂性的详细说明,请参阅官方文档:Python 2 - Unicode HOWTO.

以下是指定文件编码的特殊注释的摘录:

Python支持在任何编码中编写Unicode文字,但您必须声明正在使用的编码.这是通过将特殊注释包含在源文件的第一行或第二行来完成的:

#!/usr/bin/env python
# -*- coding: latin-1 -*-

u = u'abcdé' print ord(u[-1])
Run Code Online (Sandbox Code Playgroud)

语法的灵感来自Emacs用于指定文件本地变量的表示法.Emacs支持许多不同的变量,但Python只支持coding.这些-*-符号向Emacs表明评论是特殊的; 它们对Python没有意义,但却是一种惯例.Python 在注释中查找coding: namecoding=name在注释中查找.

如果您不包含此类注释,则使用的默认编码为ASCII.

如果您掌握了" 学习Python,第5版 " 这本书,我建议您阅读第VIII部分中的第37章"Unicode和字节字符串".高级主题.它包含在两代Python中使用Unicode文本的详细说明.

值得一提的另一个细节是,如果格式字符串是,则format始终返回一个ascii字符串ascii,无论参数是什么unicode.

与此相反,旧样式格式化如果有任何参数则%返回一个unicode字符串unicode.所以不要写这个

print(u"Writers: {}".format(writers))
Run Code Online (Sandbox Code Playgroud)

你可以写这个,它不仅更短更漂亮,而且适用于Python 2和3:

print("Writers: %s" % writers)
Run Code Online (Sandbox Code Playgroud)