python编码

[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 是4E25100111000100101),根据上表,可以发现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) 将会变成 “\世&#30028” (因为十六进制的4e16是十进制的19990,十六进制的754c是十进制的30028 )。如果需要将返回的值来输出到 html 文件中的话,将会非常有用。

>>> my_unicode.encode("ascii", "xmlcharrefreplace")
'HI &#19990;&#30028;'

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://home.unicode.org/

https://pycoders-weekly-chinese.readthedocs.io/en/latest/issue5/unipain.html

https://www.cnblogs.com/jessonluo/p/4801580.html

本文作者:朝圣

本文链接:www.zh-noone.cn/2019/12/python编码

版权声明:本博客所有文章除特别声明外,均采用CC BY-NC-SA 3.0许可协议。转载请注明出处!

windows10激活office与visio
0 条评论