Unicode
什么是 Unicode
标准 unicode
标准 Unicode 为每个字符提供了一个独特的数字,并且跨平台、设备、应用或者编程语言都是通用的。 -- 来自 http://unicode.org/standard/WhatIsUnicode.html
Unicode 之前的编码
比如 ASCII、GBK等等。
这些早期的字符编码是受限制的并且不能包含包含全世界语言的编码。
早期的字符编码互相之间也会冲突。两种编码可能使用同样的数字来表示不同的字符或者使用不同的数字来表示同样的字符。任意给定的计算机(尤其是服务器)会需要支持多种不同的编码。然而当数据在不同计算机或不同编码之间传递的时候,数据会有冲突的风险。 -- 来自 https://zh.wikipedia.org/wiki/Unicode#实现方式
UTF
UTF(Unicode Transformation Format) 的意思是 Unicode 转换格式。
例如,如果一个仅包含基本7位ASCII字符的Unicode文件,如果每个字符都使用2字节的原Unicode编码传输,其第一字节的8位始终为0。这就造成了比较大的浪费。对于这种情况,可以使用UTF-8编码,这是一种变长编码,它将基本7位ASCII字符仍用7位编码表示,占用一个字节(首位补0)。而遇到与其他Unicode字符混合的情况,将按一定算法转换,每个字符使用1-3个字节编码,并利用首位为0或1进行识别。这样对以7位ASCII字符为主的西文文档就大幅节省了编码长度(具体方案参见UTF-8)。类似的,对未来会出现的需要4个字节的辅助平面字符和其他UCS-4扩充字符,2字节编码的UTF-16也需要通过一定的算法进行转换。 -- 来自 https://zh.wikipedia.org/wiki/Unicode#实现方式
Python 编码
Unicode 是一连串的数字。
Python 编码指将 Unicode 转换为 bytes。 -- 来自 https://docs.python.org/3/howto/unicode.html#encodings
对于 ASCII 编码:
- 如果编码点小于 128,每个比特与编码点的值相同
- 如果编码点大于等于 128,那么这些 Unicode 字符不能使用这种编码表示。(Python 会抛出 UnicodeEncodeError)
-- 来自 https://zh.wikipedia.org/wiki/Unicode#实现方式
UTF-8 是最常用的编码,有如下方便的性质:
- 可以处理所有 Unicode 编码点。
- ASCII 文本也是有效的 UTF-8 文本。
- UTF-8 很紧凑;常用的字符可以使用一个或者两个 bytes 表示。
-- 来自 https://zh.wikipedia.org/wiki/Unicode#实现方式
Python3 对 Unicode 的支持
从 Python 3.0 开始,使用 Unicode 储存字符串。
Python 源码的默认编码是 UTF-8,也可以通过 # -*- coding: <encoding name> -*-
来指定特殊的编码。
读写 Unicode 数据
Unicode 数据在写入磁盘或者发送到一个 socket 前通常会被转化为一种编码。你可以自己完成所有的工作:打开一个文件,从文件中读取 8-bit bytes 然后使用
bytes.decode(encoding)
转换 bytes。但是不推荐手动处理。
一个原因是一个 Unicode 字符可以被多个 bytes 表示。如果你读取任意大小的块(比如 1024 或者 4096 bytes),你需要写错误处理代码来捕捉块的末尾部分 Unicode 字符不完整的情况。一个解决办法是读取整个文件到内存中,但是这会使你不能处理大文件。
解决办法是使用低级别的解码接口来捕捉部分编码序列的情况。这个工作已经被自带的
open()
函数实现了,open(filename, encoding=encoding)
返回一个可以拥有如read()
和write()
等方法的 file-like 对象。
-- 以上引用来自 https://docs.python.org/3/howto/unicode.html#reading-and-writing-unicode-data
Unicode 在编程中的技巧
软件内部应该只使用 Unicode 字符串,尽快解码输入数据(bytes)并只在最后给输出编码。
当使用来自浏览器或者其他不信任来源的数据时,一个常用的技巧是在使用字符串作为命令行或者储存字符串到数据库前检查字符串中的非法字符。如果你打算这样做,要注意检查解码后的字符串,而不是编码的 bytes 数据;因为一些编码可能有一些有趣的属性,比如有多个意思或者不是完全适配 ASCII。 -- 来自 https://docs.python.org/3/howto/unicode.html#tips-for-writing-unicode-aware-programs
未知编码的文件
如果你知道文件的编码是适配 ASCII 的并且只想测试或修改 ASCII 的部分,你可以用
surrogateescape
错误处理器来打开文件。
with open(fname, 'r', encoding="ascii", errors="surrogateescape") as f:
data = f.read()
# make changes to the string 'data'
with open(fname + '.new', 'w',
encoding="ascii", errors="surrogateescape") as f:
f.write(data)
surrogateescape
错误处理器将所有非 ASCII bytes 解码为 Unicode 编码点。这些秘密编码点会变回同样的 bytes 当使用surrogateescape
编码数据并写出的时候。
-- 来自 https://docs.python.org/3/howto/unicode.html#files-in-an-unknown-encoding
假设文件只有一种编码,那么可以尝试使用 所有标准编码 进行解码,从解码没有报错的结果中挑选出合适的,即没有乱码的结果。