【问题标题】:Python default string encodingPython 默认字符串编码
【发布时间】:2018-10-04 03:15:45
【问题描述】:

Python 何时、何地以及如何对字符串隐式应用编码或隐式转码(转换)?

那些“默认”(即隐含的)编码是什么?

例如,编码是什么:

  • 字符串字面量?

      s = "Byte string with national characters"
      us = u"Unicode string with national characters"
    
  • 在与 Unicode 进行类型转换时的字节字符串?

      data = unicode(random_byte_string)
    
  • 何时向文件或终端写入/写入字节和 Unicode 字符串?

      print(open("The full text of War and Peace.txt").read())
    

【问题讨论】:

  • 你真的需要了解 Python 2.x 和 3.x 吗?因为答案非常不同——而且对于 3.x 来说要简单得多,对于 3.7+ 来说甚至更简单。
  • 说到3.7+,我对此一无所知,欢迎补充。
  • 对于 2.x 和 3.x 有单独的规范答案(在顶部相互链接)不是更好吗?这样一来,那些对 3.x 有问题但没有移植或编写跨版本代码的人不需要费力地翻阅一大堆令人困惑且与他们无关的东西,只是为了找到对他们来说很重要的部分代码。
  • 最好在元主题中讨论。到目前为止,我只收到了对该帖子的支持。

标签: python python-3.x character-encoding python-2.x python-unicode


【解决方案1】:

这里涉及 Python 功能的多个部分:读取源代码并解析字符串文字转码打印。每个都有自己的约定。

简答:

  • 出于代码解析的目的:
    • str (Py2) -- 不适用,从文件中获取原始字节
    • unicode (Py2)/str (Py3) -- “源编码”,默认为ascii (Py2) 和utf-8 (Py3)
    • bytes (Py3) -- 文字中禁止使用非 ASCII 字符
  • 为了转码:
    • 两者 (Py2) -- sys.getdefaultencoding() (ascii 几乎总是)
      • 存在隐式转换,通常会导致UnicodeDecodeError/UnicodeEncodeError
    • both (Py3) -- 无,转换时必须明确指定编码
  • 对于 I/O:
    • unicode (Py2) -- <file>.encoding 如果设置,否则 sys.getdefaultencoding()
    • str (Py2) -- 不适用,写入原始字节
    • str (Py3) -- <file>.encoding,始终设置并默认为 locale.getpreferredencoding()
    • bytes (Py3) -- 无,printing 生成它的 repr()

首先,澄清一些术语,以便您正确理解其余部分。 解码是从字节字符(Unicode或其他)的翻译,而编码(作为一个进程)是逆转。请参阅The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!) – Joel on Software 了解区别。

现在...

读取源代码并解析字符串字面量

At the start of a source file, you can specify the file's "source encoding"(具体效果在后面介绍)。如果未指定,Python 2 的默认值为ascii,Python 3 的默认值为utf-8。UTF-8 BOM 与utf-8 编码声明的效果相同。

Python 2

Python 2 将源代码读取为原始字节。它只在看到一个 Unicode 文字时使用“源编码”来解析一个 Unicode 文字。 (It's more complicated than that under the hood,但这是净效应。)

> type t.py
# Encoding: cp1251
s = "абвгд"
us = u"абвгд"
print repr(s), repr(us)
> py -2 t.py
'\xe0\xe1\xe2\xe3\xe4' u'\u0430\u0431\u0432\u0433\u0434'

<change encoding declaration in the file to cp866, do not change the contents>
> py -2 t.py
'\xe0\xe1\xe2\xe3\xe4' u'\u0440\u0441\u0442\u0443\u0444'

<transcode the file to utf-8, update declaration or replace with BOM>
> py -2 t.py
'\xd0\xb0\xd0\xb1\xd0\xb2\xd0\xb3\xd0\xb4' u'\u0430\u0431\u0432\u0433\u0434'

因此,常规字符串将包含文件中的确切字节。Unicode 字符串将包含使用“源编码”解码文件字节的结果。

如果解码失败,你会得到一个SyntaxError。如果在没有指定编码的情况下文件中有非 ASCII 字符,则相同。最后,如果使用unicode_literals future,任何常规字符串文字(in that file only)在解析时都会被视为Unicode文字,这意味着什么。

Python 3

Python 3 使用“源编码”将整个源文件解码为一系列 Unicode 字符。之后进行任何解析。 (特别是,这使得在标识符中使用 Unicode 成为可能。)由于现在所有字符串文字都是 Unicode,因此不需要额外的转码。在字节文字中,禁止使用非 ASCII 字符(此类字节必须使用转义序列指定),完全规避了这个问题。

转码

根据开头的说明:

  • str (Py2)/bytes (Py3) -- bytes => 只能是decoded (直接,即;详情如下)
  • unicode (Py2)/str (Py3) -- 字符 =>只能是encoded

Python 2

在这两种情况下,如果未指定编码,则使用sys.getdefaultencoding()。它是ascii (除非您取消注释site.pyor do some other hacks 中的代码块are a recipe for disaster。所以,为了转码,sys.getdefaultencoding() 是“字符串的默认编码”。

现在,这里有一个警告:

  • decode()encode()——使用默认编码——在转换 str&lt;-&gt;unicode 时隐式完成:

    • 字符串格式(StackOverflow 上三分之一的UnicodeDecodeError/UnicodeEncodeError 问题与此有关)
    • 当尝试encode()strdecode()unicode(堆栈溢出问题的第二个三分之一)时

Python 3

根本没有“默认编码”:现在禁止strbytes 之间的隐式转换。

  • bytes 只能是decoded 和str -- encoded,encoding 参数是强制性的。
  • 转换bytes-&gt;str(包括隐式)会生成其repr()(仅对调试打印有用),从而完全避免了编码问题
  • 禁止转换str-&gt;bytes

打印

This matter 与变量的值无关,但与您在printed 时在屏幕上看到的内容有关——以及在printing 时您是否会得到UnicodeEncodeError

Python 2

  • unicodeencoded,如果设置了&lt;file&gt;.encoding;否则,它会按照上述方式隐式转换为str。 (UnicodeEncodeError SO 问题的最后三分之一属于此处。)
    • 对于标准流,流的编码是在启动时从各种特定于环境的源中猜测出来的,并且可以用 PYTHONIOENCODING 环境变量覆盖。
  • str 的字节按原样发送到 OS 流。您将在屏幕上看到哪些特定字形取决于您终端的编码设置(如果它是 UTF-8 之类的东西,如果您打印一个无效的 UTF-8 字节序列,您可能什么也看不到)。

Python 3

变化是:

  • 现在files 打开文本与二进制mode 相应地接受strbytes,并且完全拒绝处理错误的类型。文本模式文件总是有一个 encoding 集,locale.getpreferredencoding(False) being the default
  • 文本流的print 仍然隐式地将所有内容转换为str,在bytes 的情况下,按照上述方式打印其repr(),完全避开了编码问题

【讨论】:

  • 那么,在索引字节字符串时,Python 3 使用什么编码?例如。 x = b'j'; x[0];输出 106(j 的 ascii 表示)。这不是使用ascii作为默认解码方式吗?
  • @ZaidGharaybeh 在字节字符串文字中,仅允许 ascii 字符和显式代码 (\xnn)。
【解决方案2】:

隐式编码作为存储字符串/数组的内部格式:你不应该关心编码。事实上,Python 解码 字符以 Python 内部方式。它大多是透明的。只是以抽象的方式想象它是一个 Unicode 文本或一个字节序列。

Python 3.x 中的内部编码因“较大”字符而异。它可以是UTF-8/ASCII(用于ASCII 字符串)、UTF-16UTF-32。当您使用字符串时,就像您有一个 Unicode 字符串(如此抽象,而不是真正的编码)。如果您不使用 C 编程或使用某些特殊功能(内存视图),您将永远无法看到内部编码。

字节只是实际内存的一个视图。 Python 解释为unsigned char。但同样,您通常应该只考虑它的序列,而不是内部编码。

Python 2 的字节和字符串为 unsigned char,Unicode 为 UCS-2(因此 65535 以上的代码点将在 Python 2 中使用两个字符 (UCS-2) 进行编码,在 Python 2 中仅使用一个字符 (UTF-32) Python 3)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-12-28
    • 2013-06-17
    • 1970-01-01
    • 2011-07-08
    • 2011-07-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多