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 字符串。
浏览网页时:
服务器会把动态生成的 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 ,里面写入 “你好” 两个汉字保存,再打开:
windows下的结果为:
linux下的结果为:
windows 下为什么是 " 浣 犲 ソ " 这三个陌生的玩意呢?查看“你好”的 16 进制表示:
注意:E4 是位置 0,A0 是位置 2,字节为单位,从 0 开始。
再查看 " 浣 犲 ソ " 的 GBK 编码 16 进制表示:
所以 windows 下的 open 函数用 GBK 编码规则解码了被 UTF-8 编码规则编码的 file4.txt 文件:
前者用两个字节表示一个汉字而后者用三个。
把 “你好” 换成 “中国” 再试一次:在第二个字节处报错了!
这是因为汉字 “中国” 的编码第三四两个字节可能没有对应的 GBK 编码字符,从而导致出错:
解决方法:
申明 open() 函数的编码方式为 'utf-8',即 encoding="utf-8" .
在读取文本文件的时候,如果 open() 函数没有声明他们如何编码,python3 会选取代码所运行的计算机操作系统的默认编码作为 open() 函数的编码方式。
windows10 大陆区域为简体中文,可在 cmd 命令行输入 “chcp” 查看代码页:
或者:
而 936 代表的就是GBK简体中文。所以我的 open() 函数默认的编码为 GBK:
而 Linux 中由于我一开始就配置好了系统默认编码,所以 linux 中能正常显示:
查看系统编码命令:locale
但是改后对文件进行覆盖写(r+ 表示可读写,光标在文件开头),有时也会出错:
如:file4.txt 文件输入中英混合的:hello中国
再对其进行覆盖写:
也会报错 UnicodeDecodeError!分析一下:
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 找不到对应的字符,自然就报错了:
编码知识了解:
编码知识了解:
所谓的 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 字节序列。
查看字符编码的网站地址:
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() #新操作之文件字节码编码