[toc]
python编码
字符集
ASCII码
字符集,一个字节八个二进制可以代表256种字符,但ASCII码只占用了一个字节的后面7位,最前面的一位统一规定为0
ASCII 码一共规定了128个字符的编码
对照表
ASCII值 | 控制字符 | ASCII值 | 控制字符 | ASCII值 | 控制字符 | ASCII值 | 控制字符 |
---|---|---|---|---|---|---|---|
0 | NUT | 32 | (space) | 64 | @ | 96 | 、 |
1 | SOH | 33 | ! | 65 | A | 97 | a |
2 | STX | 34 | " | 66 | B | 98 | b |
3 | ETX | 35 | # | 67 | C | 99 | c |
4 | EOT | 36 | $ | 68 | D | 100 | d |
5 | ENQ | 37 | % | 69 | E | 101 | e |
6 | ACK | 38 | & | 70 | F | 102 | f |
7 | BEL | 39 | , | 71 | G | 103 | g |
8 | BS | 40 | ( | 72 | H | 104 | h |
9 | HT | 41 | ) | 73 | I | 105 | i |
10 | LF | 42 | * | 74 | J | 106 | j |
11 | VT | 43 | + | 75 | K | 107 | k |
12 | FF | 44 | , | 76 | L | 108 | l |
13 | CR | 45 | - | 77 | M | 109 | m |
14 | SO | 46 | . | 78 | N | 110 | n |
15 | SI | 47 | / | 79 | O | 111 | o |
16 | DLE | 48 | 0 | 80 | P | 112 | p |
17 | DCI | 49 | 1 | 81 | Q | 113 | q |
18 | DC2 | 50 | 2 | 82 | R | 114 | r |
19 | DC3 | 51 | 3 | 83 | S | 115 | s |
20 | DC4 | 52 | 4 | 84 | T | 116 | t |
21 | NAK | 53 | 5 | 85 | U | 117 | u |
22 | SYN | 54 | 6 | 86 | V | 118 | v |
23 | TB | 55 | 7 | 87 | W | 119 | w |
24 | CAN | 56 | 8 | 88 | X | 120 | x |
25 | EM | 57 | 9 | 89 | Y | 121 | y |
26 | SUB | 58 | : | 90 | Z | 122 | z |
27 | ESC | 59 | ; | 91 | [ | 123 | { |
28 | FS | 60 | < | 92 | / | 124 | | |
29 | GS | 61 | = | 93 | ] | 125 | } |
30 | RS | 62 | > | 94 | ^ | 126 | ` |
31 | US | 63 | ? | 95 | _ | 127 | DEL |
非ASCII码
ASCII码英文是够了,但是中文、法语呢?有些利用ASCII高位0是符号增加到了256个
Unicode
Unicode 只是一个字符集,现在的规模可以容纳100多万个符号, 汉字严
的 Unicode 是十六进制数4E25
,转换成二进制数足足有15位(100111000100101
),再次强调Unicode规定了符号的二进制,并没有规定这个二进制代码怎么存储,即没有规定多少字节为一个符号, 英文字母只用一个字节表示就够了,如果 Unicode 统一规定,每个符号用三个或四个字节表示,那么每个英文字母前都必然有二到三个字节是0,比如A的二进制为01000001(ASCII码),但是00001000001在Unicode中也可以代表A。所以ASCII也可以说是Unicode的子集
所以需要一种统一的编码方式
编码规则
不同的编码规则对于同一个二进制数字可以被解释成不同的符号(Unicode 只是一个字符集)
UTF-8
UTF-8 就是在互联网上使用最广的一种 Unicode 的实现方式。其他实现方式还包括 UTF-16(字符用两个字节或四个字节表示)和 UTF-32(字符用四个字节表示),不过在互联网上基本不用。重复一遍,这里的关系是,UTF-8 是 Unicode 的实现方式之一。
实现方式
可以使用1~4个字节表示一个符号,根据不同的符号而变化字节长度。
UTF-8 的编码规则很简单,只有二条:
1)对于单字节的符号,字节的第一位设为0
,后面7位为这个符号的 Unicode 码。因此对于英语字母,UTF-8 编码和 ASCII 码是相同的。
2)对于n
字节的符号(n > 1
),第一个字节的前n
位都设为1
,第n + 1
位设为0
,后面字节的前两位一律设为10
。剩下的没有提及的二进制位,全部为这个符号的 Unicode 码。
下表总结了编码规则,字母x
表示可用编码的位。
Unicode符号范围 | UTF-8编码方式 |
---|---|
(十六进制) | (二进制) |
0000 0000-0000 007F | 0xxxxxxx |
0000 0080-0000 07FF | 110xxxxx 10xxxxxx |
0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx |
0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx |
根据上表,解读 UTF-8 编码非常简单。如果一个字节的第一位是0
,则这个字节单独就是一个字符;如果第一位是1
,则连续有多少个1
,就表示当前字符占用多少个字节。
下面,还是以汉字严
为例,演示如何实现 UTF-8 编码。
严
的 Unicode 是4E25
(100111000100101
),根据上表,可以发现4E25
处在第三行的范围内(0000 0800 - 0000 FFFF
),因此严
的 UTF-8 编码需要三个字节,即格式是1110xxxx 10xxxxxx 10xxxxxx
。然后,从严
的最后一个二进制位开始,依次从后向前填入格式中的x
,多出的位补0
。这样就得到了,严
的 UTF-8 编码是11100100 10111000 10100101
,转换成十六进制就是E4B8A5
。
优势
一种变长的编码方式
ASCII编码实际上可以被看成是UTF-8编码的一部分
总结
encode和decode
encode 或者 decode 时候的第二个参数指明了规则。
本小结使用Python2做例子,因为python2中str有encode/decode
默认的值是 "strict ":编码、解码的时候遇到处理不了的数据则抛 UnicodeDecodeError
>>> # python2
...
>>> my_unicode=u"HI 世界"
>>> my_utf8=my_unicode.encode('utf-8')
>>> my_utf8
'HI \xe4\xb8\x96\xe7\x95\x8c'
>>> my_utf8.encode("ascii")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe4 in position 3: ordinal not in range(128)
>>> my_unicode.decode("ascii")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode characters in position 3-4: ordinal not in range(128)
"ignore": 直接将不能解/编码的丢掉
>>> my_unicode.encode("ascii", "ignore")
'HI '
>>> my_utf8.decode("ascii", "ignore")
u'HI '
" replace ": 失败时将会返回一个标准的替代字符。当编码的时候,替代值是一个问号,所以任何不能被编码的值将会产生一个"?";当解码的时候, 替代值是 Unicode U+FFFD ,给有问题的 bytes 来直接替换成”\ufffd”
```python
my_unicode.encode("ascii", "replace") 'HI ??' my_utf8.decode("ascii", "replace") u'HI \ufffd\ufffd\ufffd\ufffd\ufffd\ufffd' ```
"xmlcharrefreplace" : 编码的时候将会产生一个完全替代的 HTML/XML 字符,所以 \u4e16\u754c("你好"的unicode) 将会变成 “\世界” (因为十六进制的4e16是十进制的19990,十六进制的754c是十进制的30028 )。如果需要将返回的值来输出到 html 文件中的话,将会非常有用。
>>> my_unicode.encode("ascii", "xmlcharrefreplace")
'HI 世界'
python2中的str和unicode
Python 2 已经试图在处理 unicode 和 byte 串的时候变得有用些。比如如果要把 Unicode 字符串和 byte 字符串来组合起来的话, Python 2 将会自动的将 byte 串来解码成 unicode 字符串,从而产生一个新的 Unicode 字符串。
比如,我们想要连接 Unicode 串 “hello” 和一个 byte 字符串 “world”。结果是一个 Unicode 的 “hello world”。在我们看来。Python 2 将 “world” 使用 ASCII codec 进行了解码。这次在解码中使用的字符集的值与 sys.getdefaultencoding() 的值相等,该值与系统有关。
>>> a=u"hello"
>>> b="world"
>>> a+b
u'helloworld'
>>> import sys
>>> sys.getdefaultencoding()
'ascii'
>>> a+b.decode("ascii")
u'helloworld'
当然,这些隐藏的编码转换不能免疫于解码错误。如果你想要连接 一个 byte 字符串和一个 unicode 字符串,并且 byte 字符串不能被解码成 ASCII 的话,将会抛出一个 UnicodeDecodeError。
这就是那些可恶的 UnicodeError 的圆圈。你的代码中包含了 unicode 和 byte 字符串,只要数据全部是 ASCII 的话,所有的转换都是正确的,一旦一个非 ASCII 字符偷偷进入你的程序,那么默认的解码将会失败,从而造成 UnicodeDecodeError 的错误。
>>> a + my_utf8
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe4 in position 3: ordinal not in range(128)
Python 2 的哲学就是 Unicode 字符串和 byte 字符串是可以混合的,它试图去通过自动转换来减轻你的负担。就像在 int 和 float 之间的转换一样, int 到 float 的转换不会失败,byte 字符串到 unicode 字符串会失败。
Python 2 悄悄掩盖了 byte 到 unicode 的转换,让程序在处理 ASCII 的时候更加简单。你付出的代价就是在处理非 ASCII 的时候将会失败。
python3中的str和unicode
跟 Python 2 类似,Python 3 也有两种类型,一个是 Unicode,一个是 byte 码。但是它们有不同的命名。
现在你从普通文本转换成 “str” 类型后存储的是一个 unicode, “bytes” 类型存储的是 byte 串。你也可以通过一个 b 前缀来制造 byte 串。
所以在 Python 2 中的 “str” 现在叫做 “bytes”,而 Python 2 中的 “unicode” 现在叫做 “str”。这比起Python 2中更容易理解,因为 Unicode 是你总想要存储的内容。而 bytes 字符串只有你在想要处理 byte 的时候得到。
Python 3 中对 Unicode 支持的最大变化就是没有对 byte 字符串的自动解码
另外如果一个 Unicode 字符串和 byte 字符串中包含的是相同的 ASCII 码,Python 2 中将认为两个是相等的,而在 Python 3 中不会
>>> a=u"A"
>>> b=b"A"
>>> # python2
...
>>> a==b
True
>>> # python3
...
>>> a==b
False
这样处理的原因之一是对读取文件的变化,Python 对于读取文件有两种方式,一种是二进制,一种是文本。在 Python 2 中,它只会影响到行尾符号,甚至在 Unix 系统上的时候,基本没有区别。
在 Python 3中。这两种模式将会返回不同的结果。当你用文本模式打开一个文件时不管你是用的 “r” 模式或者默认的模式,读取成功的文件将会自动转码成 unicode ,你会得到 str 对象。
如果你用二进制模式打开一个文件,在参数中输入 “rb” ,那么从文件中读取的数据会是 bytes,对它们没有任何处理。
隐式的对 bytes 到 unicode 的处理使用的是 locale.getpreferedencoding() ,然而它有可能输出你不想要的结果。比如,当你读取 hi_utf8.txt 时,他被解码成语言偏好中所设置的编码方式,如果我们这些例子在 Windows 中创建的话,那么就是 “cp1252” 。像 ISO 8859-1, CP-1252 这些可以得到任意的 byte 值,所以不会抛出 UnicodeDecodeError ,当然也意味着它们会直接将数据解码成 CP-1252,制造出我们并不需要的垃圾信息。
为了文件读取正确的话,你应该指明想要的编码。open 函数现在已经可以通过参数来指明编码。
C:\Users\xxx>chcp #查找操作系统使用的编码
活动代码页: 936
>>> import sys, locale
>>> locale.getpreferredencoding() #这个是最重要的
'cp936'
>>> open('cafe.txt','w',encoding='utf-8').write('café')
>>> my_file = open('cafe.txt','r')
>>> type(my_file)
_io.TextIOWrapper
>>> my_file.encoding #文件对象默认使用locale.getpreferreddecoding()的值
'cp936'
>>> sys.getdefaultencoding() #如果Python需要把二进制数据转为字符对象,那么在缺省情况下使用该值。
'utf-8'
>>> sys.getfilesystemencoding() #Python用来编码或者解码文件名(不是文件内容)的时候,默认使用该编码。
'utf-8'
python3文件读写的编码
>>> open('cafe.txt','w',encoding='utf-8').write('café')
4
>>> fp = open('cafe.txt','r')
>>> fp.read()
'caf茅'
>>> fp.encoding
'cp936'
>>> open('cafe.txt','r', encoding = 'cp936').read()
'caf茅'
>>> open('cafe.txt','r', encoding = 'latin1').read()
'café'
>>> fp = open('cafe.txt','r', encoding = 'utf-8')
>>> fp.encoding
'utf-8'
>>> fp.read()
'café'
编码/解码含义
编码就是将字符串转换成字节码,涉及到字符串的内部表示;解码就是将字节码转换为字符串,将比特位显示成字符
encode编码: --> byte
decode解码: -->unicode
文件头声明作用
文件头声明编码# -- coding: utf-8 --作用。
- python2中如果没有此文件编码类型的声明,则python默认以ASCII编码去处理,所以python2中如果出现中文则异常,python3默认已经支持中文所以不加也可以
参考链接
https://tool.oschina.net/commons?type=4
http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html
https://pycoders-weekly-chinese.readthedocs.io/en/latest/issue5/unipain.html