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

新手上路,请多包涵

以下代码适用于 Python 3:

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

并产生以下输出:

 Nicholas Gyeney, André
Writers: Nicholas Gyeney, André

但是,在 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)

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

 b'Nicholas Gyeney, Andr\xc3\xa9'
Writers: b'Nicholas Gyeney, Andr\xc3\xa9'

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

 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))

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

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

原文由 B Faley 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 780
2 个回答

首先我们假设你想要支持 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))

输出:

 <type 'str'>
23

这是发生了什么变化:

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

它在 Python 2.7.12 和 3.5.2 中工作得很好。

But be warned that removing the u prefixes will make python use regular str type instead of unicode (see output of 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))

输出:

 <type 'unicode'>
22

注意: 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])
>
> ```
>
> 该语法的灵感来自 Emacs 用于指定文件局部变量的符号。 Emacs 支持许多不同的变量,但 Python 只支持 `coding` 。 `-*-` 符号向 Emacs 表明注释是特殊的;它们对 Python 没有意义,而是一种约定。 Python 在注释中查找 `coding: name` 或 `coding=name` 。
>
> 如果您不包含这样的注释,则使用的默认编码将是 ASCII。

如果您拿到了《 [学习 Python,第 5 版](http://shop.oreilly.com/product/0636920028154.do)》这本书,我鼓励您阅读第 VIII 部分的第 37 章“Unicode 和字节串”。高级主题。它包含有关在两代 Python 中使用 Unicode 文本的详细说明。

Another detail worth mentioning is that `format` always returns an `ascii` string if the format string was `ascii` , no matter that the arguments were in `unicode` 。

与此相反,如果任何参数是 \-\-\- 的旧样式格式返回 `%` `unicode` 字符串 `unicode` 。所以与其写这个

print(u”Writers: {}“.format(writers))


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

print(“Writers: %s” % writers)

”`

原文由 quasoft 发布,翻译遵循 CC BY-SA 3.0 许可协议

您可以在格式化时提供 Unicode 前缀:

 print(u"Writers: {}".format(writers))

这确实解决了这个问题,但是,您在 Python 3 脚本中乱扔了不必要的 u'' 前缀。

您也可以 from __future__ import unicode_literals 在检查版本之后,但我不会那样做,使用它通常比较棘手,并且由于 u'' 前缀足以完成工作,因此已被考虑弃用。

原文由 Dimitris Fasarakis Hilliard 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题