python:编码格式与 Unicode

文章详细介绍了Python中字符串的内部存储方式,强调了Unicode编码的重要性。讨论了不同的文本编码格式,如latin-1、UTF-32和UTF-8,特别是UTF-8的无字节顺序问题以及BOM在UTF-8编码中的作用。还提到了Microsoft的utf-8-sig变体及其在文件编码检测中的用途。
摘要由CSDN通过智能技术生成

python:编码格式与 Unicode


字符串在系统内部存储为 U+0000–U+10FFFF 范围内的码位序列。 (请参阅 PEP 393 了解有关实现的详情。) 一旦字符串对象要在 CPU 和内存以外使用,字节的大小端顺序和字节数组的存储方式就成为一个影响因素。 如同使用其他编解码器一样,将字符串序列化为字节序列被称为 编码,而从字节序列重建字符串被称为 解码。 存在许多不同的文本序列化编解码器,它们被统称为 文本编码格式。

最简单的文本编码格式 (称为 ‘latin-1’ 或 ‘iso-8859-1’) 将码位 0–255 映射为字节值 0x0–0xff,这意味着包含 U+00FF 以上码位的字符串对象无法使用此编解码器进行编码。 这样做将引发 UnicodeEncodeError,其形式类似下面这样(不过详细的错误信息可能会有所不同): UnicodeEncodeError: ‘latin-1’ codec can’t encode character ‘\u1234’ in position 3: ordinal not in range(256)。

还有另外一组编码格式(所谓的字符映射编码)会选择全部 Unicode 码位的不同子集并设定如何将这些码位映射为字节值 0x0–0xff。 要查看这是如何实现的,只需简单地打开相应源码例如 encodings/cp1252.py (这是一个主要在 Windows 上使用的编码格式)。 其中会有一个包含 256 个字符的字符串常量,指明每个字符所映射的字节值。

所有这些编码格式只能对 Unicode 所定义的 1114112 个码位中的 256 个进行编码。 一种能够存储每个 Unicode 码位的简单而直接的办法就是将每个友们存储为四个连续的字节。 存在两种不同的可能性:以大端序存储或以小端序存储。 这两种编码格式分别被称为 UTF-32-BE 和 UTF-32-LE。 他们共有的缺点可以举例说明:如果你在一台小端序的机器上使用 UTF-32-BE 则你必须在编码和解码时翻转字节。 UTF-32 避免了这个问题:字节的排列将总是使用自然端序。 当这些字节被具有不同端序的 CPU 读取时,则必须进行字节翻转。 为了能够检测 UTF-16 或 UTF-32 字节序列的大小端序,可以使用所谓的 BOM (“字节顺序标记”)。 这对应于 Unicode 字符 U+FEFF。 此字符可被添加到每个 UTF-16 或 UTF-32 字节序列的开头。 此字符的字节翻转版本 (0xFFFE) 是一个不可出现于 Unicode 文本中的非法字符。 因此当发现一个 UTF-16 或 UTF-32 字节序列的首个字符是 U+FFFE 时,就必须在解码时进行字节翻转。 不幸的是字符 U+FEFF 还有第二个含义 ZERO WIDTH NO-BREAK SPACE: 即宽度为零并且不允许用来拆分单词的字符。 它可以被用来为语言分析算法提供提示。 在 Unicode 4.0 中使用 U+FEFF 表示 ZERO WIDTH NO-BREAK SPACE 已被弃用 (改用 U+2060 (WORD JOINER) 负责此任务)。 然而 Unicode 软件仍然必须能够处理 U+FEFF 的两个含义:作为 BOM 它被用来确定已编码字节的存储布局,并在字节序列被解码为字符串后将其去除;作为 ZERO WIDTH NO-BREAK SPACE 它是一个普通字符,将像其他字符一样被解码。

还有另一种编码格式能够对所有 Unicode 字符进行编码:UTF-8。 UTF-8 是一种 8 位编码,这意味着在 UTF-8 中没有字节顺序问题。 UTF-8 字节序列中的每个字节由两部分组成:标志位(最重要的位)和内容位。 标志位是由零至四个值为 1 的二进制位加一个值为 0 的二进制位构成的序列。 Unicode 字符会按以下形式进行编码(其中 x 为内容位,当拼接为一体时将给出对应的 Unicode 字符):
在这里插入图片描述
Unicode 字符最不重要的一个位就是最右侧的二进制位 x。

由于 UTF-8 是一种 8 位编码格式,因此 BOM 是不必要的,并且已编码字符串中的任何 U+FEFF 字符(即使是作为第一个字符)都会被视为是 ZERO WIDTH NO-BREAK SPACE。

在没有外部信息的情况下将不可能毫无疑义地确定一个字符串使用了何种编码格式。 每种字符映射编码格式都可以解码任意的随机字节序列。 然而对 UTF-8 来说这却是不可能的,因为 UTF-8 字节序列具有不允许任意字节序列的特别结构。 为了提升 UTF-8 编码格式检测的可靠性,Microsoft 发明了一种 UTF-8 的变体形式 (Python 称之为 “utf-8-sig”) 专门用于其 Notepad 程序:在任何 Unicode 字节被写入文件之前,会先写入一个 UTF-8 编码的 BOM (它看起来是这样一个字节序列: 0xef, 0xbb, 0xbf)。 由于任何字符映射编码后的文件都不大可能以这些字节值开头 (例如它们会映射为

LATIN SMALL LETTER I WITH DIAERESIS
RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
INVERTED QUESTION MARK
对于 iso-8859-1 编码格式来说),这提升了根据字节序列来正确猜测 utf-8-sig 编码格式的成功率。 所以在这里 BOM 的作用并不是帮助确定生成字节序列所使用的字节顺序,而是作为帮助猜测编码格式的记号。 在进行编码时 utf-8-sig 编解码器将把 0xef, 0xbb, 0xbf 作为头三个字节写入文件。 在进行解码时 utf-8-sig 将跳过这三个字节,如果它们作为文件的头三个字节出现的话。 在 UTF-8 中并不推荐使用 BOM,通常应当避免它们的出现。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一个天秤座的程序猿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值