Following system colour scheme - Python 增强提案 Selected dark colour scheme - Python 增强提案 Selected light colour scheme - Python 增强提案

Python 增强提案

PEP 540 – 添加新的 UTF-8 模式

作者:
Victor Stinner <vstinner at python.org>
BDFL 委托
INADA Naoki
状态:
最终版
类型:
标准跟踪
创建日期:
2016年1月5日
Python 版本:
3.7
决议:
Python-Dev 消息

目录

摘要

添加一个新的“UTF-8 模式”以增强 Python 对 UTF-8 的使用。当 UTF-8 模式激活时,Python 将

  • 使用 utf-8 编码,无论当前平台设置的区域设置如何,并且
  • stdinstdout 的错误处理程序更改为 surrogateescape

此模式默认关闭,但在使用“POSIX”区域设置时会自动激活。

添加 -X utf8 命令行选项和 PYTHONUTF8 环境变量来控制 UTF-8 模式。

基本原理

区域设置编码和 UTF-8

Python 3.6 对文件名、环境变量、标准流等使用区域设置编码。区域设置编码继承自区域设置;编码和区域设置紧密耦合。

许多用户从 POSIX 区域设置(又称“C”区域设置)继承 ASCII 编码,但由于各种原因无法更改区域设置。这种编码在 Unicode 支持方面非常有限:任何非 ASCII 字符都可能导致问题。

获取准确的区域设置并不总是那么容易。区域设置在不同的 Linux 发行版、FreeBSD、macOS 等上没有完全相同的名称。而且某些区域设置,例如最近的 C.UTF-8 区域设置,仅受少数平台支持。当前区域设置甚至可以在同一平台上根据上下文而变化;例如,SSH 连接可以使用与同一机器上的文件系统或本地终端编码不同的编码。

另一方面,Python 3.6 在 macOS、Android 和 Windows 上已经默认使用 UTF-8(PEP 529),用于大多数函数——尽管 open() 是一个显著的例外。UTF-8 也是 Python 脚本、XML 和 JSON 文件格式的默认编码。Go 编程语言对所有字符串都使用 UTF-8。

UTF-8 支持在现代平台读写数据方面几乎无处不在。它在 Python 中也具有出色的支持。问题仅仅是区域设置经常配置错误。一个显而易见的解决方案是:忽略区域设置编码并使用 UTF-8。

不可解码字节的直通:surrogateescape

当使用默认的 strict 错误处理程序从 UTF-8 解码字节时,Python 3 会在第一个不可解码字节上引发 UnicodeDecodeError

Unix 命令行工具,如 catgrep,以及大多数 Python 2 应用程序根本没有这类错误:它们不解码数据,而是将数据作为原始字节序列处理。

Python 3 已经有一个解决方案可以像 Unix 工具和 Python 2 一样工作:surrogateescape 错误处理程序(PEP 383)。它允许将数据视为字节处理,但实际上使用 Unicode;不可解码的字节存储为代理字符。

UTF-8 模式为 stdinstdout 设置 surrogateescape 错误处理程序,因为这些流通常与 Unix 命令行工具相关联。

然而,用户对文件有不同的期望。文件应正确编码,并且当 open() 以错误选项调用时,例如以文本模式打开 JPEG 图片,Python 应尽早失败。出于这些原因,open() 的默认错误处理程序仍为 strict

默认不作更改以实现最佳向后兼容性

虽然 UTF-8 在大多数情况下是完美的,但有时区域设置编码实际上是最好的编码。

本 PEP 更改了 POSIX 区域设置的行为,因为此区域设置通常等同于 ASCII 编码,而 UTF-8 是一个更好的选择。它不更改其他区域设置的行为,以防止任何风险或回归。

由于用户有责任为这些其他区域设置明确启用新的 UTF-8 模式,因此他们对 UTF-8 模式引起的任何潜在乱码问题负责。

提案

添加一个新的 UTF-8 模式以使用 UTF-8 编码,忽略区域设置编码,并将 stdinstdout 错误处理程序更改为 surrogateescape

添加新的 -X utf8 命令行选项和 PYTHONUTF8 环境变量。用户可以使用命令行选项 -X utf8 或通过设置环境变量 PYTHONUTF8=1 来明确激活 UTF-8 模式。

此模式默认禁用,并由 POSIX 区域设置启用。用户可以使用命令行选项 -X utf8=0 或通过设置环境变量 PYTHONUTF8=0 来明确禁用 UTF-8 模式。

对于标准流,PYTHONIOENCODING 环境变量优先于 UTF-8 模式。

在 Windows 上,PYTHONLEGACYWINDOWSFSENCODING 环境变量(PEP 529)优先于 UTF-8 模式。

UTF-8 模式的效果

  • sys.getfilesystemencoding() 返回 'UTF-8'
  • locale.getpreferredencoding() 返回 UTF-8;其 do_setlocale 参数和区域设置编码被忽略。
  • sys.stdinsys.stdout 错误处理程序设置为 surrogateescape

副作用

  • open() 默认使用 UTF-8 编码。但是,它仍然默认使用 strict 错误处理程序。
  • os.fsdecode()os.fsencode() 使用 UTF-8 编码。
  • 命令行参数、环境变量和文件名使用 UTF-8 编码。

与区域设置强制转换的关系 (PEP 538)

POSIX 区域设置启用区域设置强制转换(PEP 538)和 UTF-8 模式(PEP 540)。当区域设置强制转换启用时,启用 UTF-8 模式没有额外的效果。

UTF-8 模式与区域设置强制转换具有相同的效果

  • sys.getfilesystemencoding() 返回 'UTF-8'
  • locale.getpreferredencoding() 返回 UTF-8,并且
  • sys.stdinsys.stdout 错误处理程序设置为 surrogateescape

这些更改只影响 Python 代码。但是区域设置强制转换有额外的效果:LC_CTYPE 环境变量和 LC_CTYPE 区域设置被设置为像 C.UTF-8 这样的 UTF-8 区域设置。一个副作用是,非 Python 代码也受区域设置强制转换的影响。这两个 PEP 相互补充。

在像 Centos 7 这样不支持区域设置强制转换的平台上,POSIX 区域设置只启用 UTF-8 模式。在这种情况下,Python 代码使用 UTF-8 编码并忽略区域设置编码,而非 Python 代码使用区域设置编码,对于 POSIX 区域设置通常是 ASCII。

虽然 UTF-8 模式在所有平台上都受支持,并且可以在任何区域设置下启用,但区域设置强制转换并非所有平台都支持,并且仅限于 POSIX 区域设置。

PYTHONUTF8 环境变量设置为 1 时,UTF-8 模式仅对 Python 子进程产生影响,而区域设置强制转换设置 LC_CTYPE 环境变量,这会影响所有子进程。

区域设置强制转换方法的优点是它有助于确保二进制扩展模块和子进程中的编码处理与 Python 的编码处理保持一致。UTF-8 模式方法的优点是它允许嵌入应用程序更改解释器的行为,而无需更改进程全局区域设置。

向后兼容性

唯一向后不兼容的更改是 POSIX 区域设置现在默认启用 UTF-8 模式:它现在将使用 UTF-8 编码,忽略区域设置编码,并将 stdinstdout 的错误处理程序更改为 surrogateescape

附录:编码和错误处理程序

UTF-8 模式更改了 open()os.fsdecode()os.fsencode()sys.stdinsys.stdoutsys.stderr 使用的默认编码和错误处理程序。

编码和错误处理程序

函数 默认值 UTF-8 模式或 POSIX 区域设置
open() locale/strict UTF-8/strict
os.fsdecode(),os.fsencode() locale/surrogateescape UTF-8/surrogateescape
sys.stdin, sys.stdout locale/strict UTF-8/surrogateescape
sys.stderr locale/backslashreplace UTF-8/backslashreplace

相比之下,Python 3.6 使用

函数 默认值 POSIX 区域设置
open() locale/strict locale/strict
os.fsdecode(),os.fsencode() locale/surrogateescape locale/surrogateescape
sys.stdin, sys.stdout locale/strict locale/surrogateescape
sys.stderr locale/backslashreplace locale/backslashreplace

Windows 上的编码和错误处理程序

在 Windows 上,编码和错误处理程序是不同的

函数 默认值 传统 Windows 文件系统编码 UTF-8 模式
open() mbcs/strict mbcs/strict UTF-8/strict
os.fsdecode(),os.fsencode() UTF-8/surrogatepass mbcs/replace UTF-8/surrogatepass
sys.stdin, sys.stdout UTF-8/surrogateescape UTF-8/surrogateescape UTF-8/surrogateescape
sys.stderr UTF-8/backslashreplace UTF-8/backslashreplace UTF-8/backslashreplace

相比之下,Python 3.6 使用

函数 默认值 传统 Windows 文件系统编码
open() mbcs/strict mbcs/strict
os.fsdecode(),os.fsencode() UTF-8/surrogatepass mbcs/replace
sys.stdin, sys.stdout UTF-8/surrogateescape UTF-8/surrogateescape
sys.stderr UTF-8/backslashreplace UTF-8/backslashreplace

“传统 Windows 文件系统编码”由 PYTHONLEGACYWINDOWSFSENCODING 环境变量启用。

如果 stdin 和/或 stdout 被重定向到管道,sys.stdin 和/或 sys.stdout 默认使用 mbcs 编码而不是 UTF-8。但在 UTF-8 模式下,sys.stdinsys.stdout 始终使用 UTF-8 编码。

注意

Windows 上没有 POSIX 区域设置。ANSI 代码页用作区域设置编码,并且此代码页从不使用 ASCII 编码。

发布历史

版本历史

  • 版本 4:在 UTF-8 模式下,locale.getpreferredencoding() 现在返回 'UTF-8'
  • 版本 3:UTF-8 模式不再更改 open() 的默认错误处理程序(strict),并且已移除严格 UTF-8 模式。
  • 版本 2:从头重写 PEP,使其更短、更容易理解。
  • 版本 1:第一个版本发布到 python-dev。

来源:https://github.com/python/peps/blob/main/peps/pep-0540.rst

最后修改:2025-02-01 08:59:27 GMT