Python编码:

中文乱码问题:

如果开头不声明保存编码的格式是什么,那么它会默认使用 ASCII 码保存文件。这时如果你的代码中有中文就会出错了,即使你的中文是包含在注释里面的。声明中文编码格式的方法是在文件开头加上如下代码。 #coding=utf-8或#coding=gbk

设置Python中的编码:

# code: 编码格式

(美观写法:#-*- coding: UTF-8 -*-)

编码类型:

GB2312编码:适用于汉字处理、汉字通信等系统之间的信息交换。

GBK编码: 是汉字编码标准之一,是在 GB2312—80 标准基础上的内码扩展规范,使用了双字节编码。

ASCII编码: 是对英语字符和二进制之间的关系做的统一规定。

Unicode编码(内存中):这是一种世界上所有字符的编码,当然,它没有规定的存储方式。(固定的最少 2 个字节。)

UTF-8编码: 是 Unicode Transformation Format-8 bit 的缩写,4UTF-8 是 Unicode 的一种实现方式。它是可变长的编码方式,可以使用 1~4个 字节表示一个字符,可以根据不同的符号而变化字节长度。(表示英文时可节省空间)

扩展:

在计算机内存中,统一使用 Unicode 编码,当需要保存到硬盘或者需要传输的时候,就转换为 UTF-8 编码。

举个例子:

当用记事本程序编辑的时候,从文件读取的 UTF-8 字符被转换为内存中的 Unicode 字符进行读取修改,编辑完成后,当保存的时候再把 Unicode 转换为 UTF-8 写入保存到 04 文件中,具体过程如图 2-4 所示。

所以在默认情况下,Python 源码文件以 UTF-8 格式进行编码,所有字符串都是 Unicode 字符串。

URI python 编码 python中的编码_ico

浏览网页时:

服务器会把动态生成的 Unicode 内容转换为 UTF-8 再传输到浏览器,具体过程如图 2-5 所示。

所以读者看到很多网页的源码上会有类似 <meta charset="UTF-8" /> 的信息,表示该网页正是用的 UTF-8 编码格式。

关于 python 内 open 函数 encoding 编码问题:

关于 python 内 open 函数 encoding 编码问题:

关于python内open函数encoding编码问题 - 天青色wy - 博客园

在学 python3.7 的 open 函数时,新建一个 file_name.txt 文本文件,输入中文保存,再用 open(file_name,'r+') 打开,再去读写时会出现编码上的问题:

1、当原文件为 utf8 编码格式,且不包含中文,则对其进行读操作,正常;对其进行写操作(非中文),正常,文件编码格式不变;

但是当写入中文字符时,文件编码格式变为 gbk ,此时 file_name.txt 文本文件会将你输入的中文显示为 16 进制数,并会提示你用 gbk 编码重载文件。

2、当原文件为 utf8 编码格式,若包含中文,此时对其进行读操作,则可能报错 UnicodeDecodeError,也可能不报错。是否报错跟中文内容有关。写入中文情况与1相同。

举例如新建一个文件 file4.txt ,里面写入 “你好” 两个汉字保存,再打开:

URI python 编码 python中的编码_URI python 编码_02

windows下的结果为:

URI python 编码 python中的编码_ctf_03

linux下的结果为:

URI python 编码 python中的编码_ctf_04

windows 下为什么是 " 浣 犲 ソ " 这三个陌生的玩意呢?查看“你好”的 16 进制表示:

注意:E4 是位置 0,A0 是位置 2,字节为单位,从 0 开始。

URI python 编码 python中的编码_python_05

再查看 " 浣 犲 ソ " 的 GBK 编码 16 进制表示:

URI python 编码 python中的编码_ctf_06

所以 windows 下的 open 函数用 GBK 编码规则解码了被 UTF-8 编码规则编码的 file4.txt 文件:

前者用两个字节表示一个汉字而后者用三个。

把 “你好” 换成 “中国” 再试一次:在第二个字节处报错了!

URI python 编码 python中的编码_进制_07

这是因为汉字 “中国” 的编码第三四两个字节可能没有对应的 GBK 编码字符,从而导致出错:

URI python 编码 python中的编码_ico_08

解决方法:

申明 open() 函数的编码方式为 'utf-8',即 encoding="utf-8" .

在读取文本文件的时候,如果 open() 函数没有声明他们如何编码,python3 会选取代码所运行的计算机操作系统的默认编码作为 open() 函数的编码方式。

windows10 大陆区域为简体中文,可在 cmd 命令行输入 “chcp” 查看代码页:

URI python 编码 python中的编码_python_09

或者:

URI python 编码 python中的编码_URI python 编码_10

而 936 代表的就是GBK简体中文。所以我的 open() 函数默认的编码为 GBK:

URI python 编码 python中的编码_ico_11

而 Linux 中由于我一开始就配置好了系统默认编码,所以 linux 中能正常显示:

查看系统编码命令:locale

URI python 编码 python中的编码_URI python 编码_12

URI python 编码 python中的编码_进制_13

但是改后对文件进行覆盖写(r+ 表示可读写,光标在文件开头),有时也会出错:

如:file4.txt 文件输入中英混合的:hello中国

URI python 编码 python中的编码_python_14

再对其进行覆盖写:

URI python 编码 python中的编码_python_15

也会报错 UnicodeDecodeError!分析一下:

URI python 编码 python中的编码_URI python 编码_16

hello 中国的 utf8 16 进制表示为:

68 65 6C 6C 6F E4 B8 AD E5 9B BD

天青色的 utf8 16 进制表示为:

E5 A4 A9 E9 9D 92 E8 89 B2

覆盖写入天青色后变成:

E5 A4 A9 E9 9D 92 E8 89 B2 9B BD

还剩两个字节 9B BD 找不到对应的字符,自然就报错了:

URI python 编码 python中的编码_URI python 编码_17

编码知识了解:

编码知识了解:

  所谓的 Unicode 内存编码其实是字符集和编码方式(utf8、utf16、utf32)以及其他属性的总称。Unicode 标准把全球的字符用唯一的 16 进制编号表示出来,这个编号就叫 “码点” 或 “码位”(Code Point),如 U+708E 表示汉字“炎”。

所有码点共占 21 个bits(一开始占 16 个 bits,2 字节,后 来不够用有所升级),范围是 0 ~ 1 0000 1111 1111 1111 1111 ,即 0x0~0x10FFFF,最多可表示 1114111 个。在最新的 Unicode13.0 版本中分配了 14 万多个码点。注意,在 0x0~0x10FFFF 范围内的任何值都是码点,但不是所有的码点都有对应的字符,如基本多语言平面中0xD800~0xDFFF 这段范围就没有对应的字符,这里面是保留给 utf-16 编码的高位代理和低位代理。

  码元(Code Unit,也称 “代码单元”)是指一个已编码的文本中具有最短的比特组合的单元。对于UTF-8 来说,码元是 8 比特长;对于 UTF-16 来说,码元是 16 比特长。

  Unicode 将每 16 位二进制数表示的范围作为一个平面,第一个平面称为基本多语言平面,用于与常用字符对应,其范围是 0000 0000 0000 0000 ~ 1111 1111 1111 1111,用十六进制表示为 0x0000 ~ 0xFFFF ;剩余十六个平面称为辅助平面,与一些辅助字符对应,如中日韩表意文字,emoji 表情,甲骨文等。

  Unicode 是内存中的固定两个字节的编码形式。而 UTF-8 则是磁盘中的省空间的可变长的编码形式。

  我们经常说 Unicode 编码,那我们为什么不能直接将 Unicode 字符集里与字符对应的码点以字节序列的形式存到计算机里面呢?就像 ASCII 编码那样直接用数字 65 来存储大写字母 “A"?

  其实可以,那就是 utf-32编码。因为 Unicode 字符集里的码点总共才占了 21 比特位,我用 3 字节 24 比特位去存绰绰有余,但是考虑到 CPU 的寄存器位数是 2 的 n 次方,所以直接用 4 字节 32 比特位来存。但这对于原本只需一个字节的英文或数字字符现在都要四个字节,其中三个字节都填充了 0,占用内存太大,实在是浪费资源。后来用 utf-16,用两个字节或四个字节来存储,不定长的,或者用 ucs-2 定长的 2 字节来存储,都不太令人满意,其中还涉及到字节序的问题。几经折腾还是觉得 utf-8 比较香。

  UTF-8 是一种不定长的 Unicode 编码方式,一个字符可能占用 1 个字节,也有可能占用 2, 3, 4 个字节。

  Unicode 字符集里的码点转化成 utf-8 编码的多字节序列过程:

  1. 单字节的字符,字节的第一位设为 0,如英文字母,UTF-8 码只占用一个字节,和 ASCII 码完全相同;

  2. n 个字节的字符 (n>1),如中文汉字,第一个字节的前 n 位设为 1,第 n+1 位设为 0,后面字节的前两位都设为10,这 n 个字节的其余空位填充该字符 unicode 码,高位用 0 补足。

  U+ 0000 ~ U+ 007F:   0XXXXXXX

  U+ 0080 ~ U+ 07FF:   110XXXXX 10XXXXXX

  U+ 0800 ~ U+ FFFF:   1110XXXX 10XXXXXX 10XXXXXX

  U+10000 ~ U+10FFFF:  11110XXX 10XXXXXX 10XXXXXX 10XXXXXX

UTF-8 解码规则也很简单。如果一个字节的第一位是 0,则这个字节单独就表示一个字符;如果第一位是 1,则连续有多少个 1,就表示当前字符占用多少个字节。

举个栗子,如:

汉字里的 “汉” 字的 Unicode 编码 16 进制表示为:

U+6C49(它占两个字节,6C 是一个字节,49 是一个字节。一个字节占 8 比特位,6 是第一个八位的前 4 位 0110),写成二进制是: 0110 1100 0100 1001。

因为 0x6C49 在 0x0800-0xFFFF 之间, 使用 3 字节模板:

1110xxxx 10xxxxxx 10xxxxxx。

我们只需要将 0110 1100 0100 1001 这个二进制数依次代替模板中的 x,得到:

11100110 10110001 10001001, 转为 16 进制即 E6 B1 89。这个就是被存到计算机磁盘中的 utf-8 字节序列。

URI python 编码 python中的编码_进制_18

查看字符编码的网站地址:

UTF-8 编码有很多优点:

1、存储文本文件到计算机硬盘节省存储空间

2、传输字符串数据时,节省宽带

3、编码是变长的,很好的兼容了 ASCII

4、容错能力强悍。哪怕字节序列损坏,它也能够跳过损坏部分,进而正确解码后面的字符。

但是 21-bits 的 code point 与 8-bits 为一个 code unit 的字节序列之间的转换需要更多系统开销。编码速度相对要慢一些。

utf-8 虽然国际通行,但是用三个字节表示一个汉字还是有点浪费空间。如果在简体中文环境下,使用 gbk 编码比 utf-8 更香。gbk 是变长编码,占 1 个或者 2 个字节,1 个字节时与 ASCII 码完全相同。

python 中 unicode 和 unicode-escape:

python 中 unicode 和 unicode-escape:

在 python中,unicode 是内存编码集,固定的 2 字节大小。一般我们将数据存储到文件时,需要将数据先编码为其他编码集,比如 utf-8、gbk 等。

读取数据的时候再通过同样的编码集进行解码即可:

#python3

>>> s = '中国'

>>> a = s.encode() #这里根据我的环境默认是utf-8编码

>>> a

b'\xe4\xb8\xad\xe5\x9b\xbd'

>>> b = a.decode()

>>> b

'中国'

但是其实还有一种 unicode-escape 编码集,他是将 unicode 内存编码值直接存储:

#python3

>>> s = '中国'

>>> b = s.encode('unicode-escape') #直接用固定的 2 字节大小的 unicode 内存编码来编码,所以展现的是内存中的字符串形式

>>> b

b'\\u4e2d\\u56fd'

>>> c = b.decode('unicode-escape')

>>> c

'中国'

拓展:还有一种 string-escape 编码集,在 2 中可以对字节流用 string-escape 进行编码

#python2

>>> s = '中国'

>>> a = s.decode('gbk')

>>> print a

中国

>>> b = s.decode('utf-8') #这里是用 utf-8 转 gbk 的编码对应不了产生错误,0xd6 是 gbk 编码的一个值

Traceback (most recent call last):

File "<stdin>", line 1, in <module>

File "D:\python\python2.7\lib\encodings\utf_8.py", line 16, in decode

return codecs.utf_8_decode(input, errors, True)

UnicodeDecodeError: 'utf8' codec can't decode byte 0xd6 in position 0: invalid c

ontinuation byte

>>> c = s.decode('string-escape')

>>> print c

中国

chardet.detect():

使用 chardet.detect() 进行编码集检测时很多时候并不准确,比如中文过少时会识别成 IBM855 编码集:

#python3

>>> s = '中国'

>>> c = s.encode('gbk')

>>> chardet.detect(c)

{'encoding': 'IBM855', 'confidence': 0.7679697235616183, 'language': 'Russian'}

注:855 OEM 西里尔语 IBM855。

中文比较多时,还是准确的:

>>> s = '中国范文芳威风威风'

>>> c = s.encode('gbk')

>>> chardet.detect(c)

{'encoding': 'GB2312', 'confidence': 0.99, 'language': 'Chinese'}

python 中 unicode 原样转成str:

python 中 unicode 原样转成 str:

利用 unicode-escape 与 string-escape

通过 BS4 抓取的数据竟然抓到了这样的字符串:

这是一个 utf8 编码的汉字,但是前面却出现了u,成了 unicode 码,导致无法正确解码。

text = u'\xe9\x95\xbf\xe5\x9f\x8e'

使用 unicode-escape 对 unicode 进行转码先:

text = text.encode('unicode-escape')

得到:text = '\\xe9\\x95\\xbf\\xe5\\x9f\\x8e'

转过来了,但是,反斜杠也被原样转了。接下来使用:(需求实现)

text = text.decode('string-escape')

现在 text 的值为:text = '\xe9\x95\xbf\xe5\x9f\x8e'

总结 string-escape 和 unicode-escape:

总结 string-escape 和 unicode-escape:

string-escape 是对二进制的字节流,一个字节一个字节转义,并对每个字节以 16 进制输出,比如:

#python2

>>> u'中'.encode('gbk')

'\xd6\xd0'

>>> u'中'.encode('gbk').encode('string-escape')

'\\xd6\\xd0'

>>> u'中'.encode('utf-8')

'\xe4\xb8\xad'

>>> u'中'.encode('utf-8').encode('string-escape') #注意,这是一个字符串 '\\xe4\\xb8\\xad',这里是将"中"的utf-8编码值( E4B8AD )输出成一个可见字符串。

'\\xe4\\xb8\\xad'

注意:

python2 中不加 u 转为 unicode 字符会报错,因为 python2 读取时转为 unicode 编码,但是保存时却没有转回 utf-8 编码。

python3 则没有这个现象:

>>> '中'.encode('utf-8') #因为这界面上显示的 '中' 在计算机中是磁盘中的也就是已经是 utf-8 的默认编码形式。0xe4 是 '中' 的 utf-8 编码的一个值,所以 utf-8 不能二次编码 。内存编码 unicode 是看不见的,因为在读取修改的内存时。

Traceback (most recent call last):

File "<stdin>", line 1, in <module>

UnicodeDecodeError: 'ascii' codec can't decode byte 0xe4 in position 0: ordinal not in range(128)

unicode-escape 是对 unicode 编码的字节流,两个字节两个字节转义,并对每两个字节一起以 16 进制输出:

#python2

>>> '中'.decode('unicode-escape') #磁盘中 utf-8 编码读取到内存中 unicode 编码后再 unicode 解码,所以变回 utf-8 编码

u'\xe4\xb8\xad'

>>> u'中'.encode('unicode-escape') #这里加了 u 后读取出来就是 unicode 内存编码,直接转义输出来而已

'\\u4e2d'

对转义反斜线,两者的效果一样!

>>> '\\\\u4e2d'.decode('unicode-escape')

u'\\u4e2d'

>>> '\\\\u4e2d'.decode('string-escape')

'\\u4e2d'

>>> '\\\\xe4\\\\xb8\\\\xad'.decode('unicode-escape')

u'\\xe4\\xb8\\xad'

>>> '\\\\xe4\\\\xb8\\\\xad'.decode('string-escape')

'\\xe4\\xb8\\xad'

深刻理解这两个字符串的含义和区别:

>>> "\\x41\\x42\\x43\\xe4\\xb8\\xad"

'\\x41\\x42\\x43\\xe4\\xb8\\xad'

>>> len("\\x41\\x42\\x43\\xe4\\xb8\\xad")

24

>>> "\x41\x42\x43\xe4\xb8\xad"

'ABC\xe4\xb8\xad'

>>> len("\x41\x42\x43\xe4\xb8\xad")

6

关于读文件:

如果文件中存储的内容是" \x41\x42\x43\xe4\xb8\xad"

那么 python 程序去读到的是字符串其实是: "\\x41\\x42\\x43\\xe4\\xb8\\xad"

>>> len("\\x41\\x42\\x43\\xe4\\xb8\\xad")

>>> 24

也就是说,读到的内容,需要先 decode("string-escape"),然后再 decode("utf-8")

其他例子:

>>> "\x41\x42\x43\xe4\xb8\xad".decode('string-escape').decode('utf-8')

u'ABC\u4e2d'

>>> "\\x41\\x42\\x43\\xe4\\xb8\\xad".decode('string-escape').decode('utf-8')

u'ABC\u4e2d'

>>> "\x41\x42\x43\xe4\xb8\xad".decode("utf-8")

u'ABC\u4e2d'

总结下:

unicode 如果转为 utf8/gbk 后,可以用 string-escape 显示出来

单纯的 unicode 编码可以直接用 unicode-escape 转码。

特殊文件的编码:

对于 exe 文件来说,其编码应该是 unicode,而不是 utf-8 编码:

所以打开文件读取输入流时应该使用 unicode-escape,而不是默认的 utf-8。

key2=open('1.exe','r',encoding = 'unicode-escape').read() #新操作之文件字节码编码