python小记——python的编码问题详解

痞子三分冷 提交于 2020-01-02 13:05:59

python文件IO操作时,对编码知识一概不知,那可真的是蛋疼了哈。utf-8啊、gbk、还有encode()、decode()等函数以及encoding参数,搞不好就乱码甚至是报错,那可是头都大了。不说了不说了先上代码先上代码。(下面代码是在Windows系统上进行演示的)

import sys
import locale
str = '小甲'
print(str)
print(type(str))
print(sys.getdefaultencoding())
print(locale.getdefaultlocale())
with open('utf','w',encoding='utf-8') as file:
    file.write(str)
with open('gbk','w',encoding='gbk') as file:
    file.write(str)

输出结果:

小甲
<class 'str'>
utf-8
('zh_CN', 'cp936')

解释:
getdefaultencoding():返回的是系统编码格式,这里的系统指的不是操作系统,而是python编译器的默认编码,python3的编码是UTF-8。
getdefaultlocale():返回的是本地编码格式,这里指的是电脑的操作系统的编码格式,Windows是GBK,Linux是UTF-8.
而上面显示的打印结果(‘zh_CN’, ‘cp936’)中,CP936其实就是GBK,IBM在发明Code Page的时候将GBK放在第936页,所以叫CP936。GBK和UTF-8简单的来说,区别就是编码方式不同,表示的文字范围不同。(UTF-8能表示更多的语言文字,更加通用)。

把上面输入了内容的两个文件打开并查看内容,如下;

with open('utf','r') as file:
    print(file.read())

with open('gbk','r') as file:
    print(file.read())

执行结果:

灏忕敳
小甲

到这里,有没有懵了呢?是不是有这么个有疑问:明明在两个文件里写入了相同的字符,查看的结果怎么会不一样呢?
其实上面两个文件写入的内容分别指定了编码格式:就是用encoding这个参数作为指定的编码格式。
打开第一个文件“utf”时指定的编码格式是“utf-8”,打开第二个文件“utf”时指定的编码格式是“gbk”;而此时打开文件的open函数并没有用encoding参数指定编码格式,所以就引发了上面输出不一样的结果的问题。下面说两个重点:
系统默认编码:在python3编译器读取.py文件时,若没有头文件编码声明,则默认使用系统默认编码“utf-8”来对.py文件进行解码。并且在调用 encode()这个函数时,不传参的话默认是“ utf-8 ”。
本地默认编码:在你编写的python3程序时,若使用了 open( )函数 ,而不给它传入 “ encoding ” 这个参数,那么会自动使用本地默认编码。没错,如果在Windows系统中,就是默认用gbk格式。
由此,就可以知道上面出现结果不一样的原因了:**开始时指定了不一样的编码格式进行写入内容,而后面都使用了默认的GBK编码格式以至于出现了不一样的结果了。要知道,编码是 “编” 和 “解” 的两个步骤,一定要一一对应才能正确解码!虽然通常我们都叫“编码格式”,这是有一定误导性的。实际上另一半是“解码格式”,要有意识地区分 “编” 和 “解” ,我们不能像网上有些文章一样将这两者混为一谈。**所以上面的第一个文件开始时用“encoding=utf-8”指定编码格式写入内容,而用默认的“GBK”格式进行解码并输出,编与解并没有一一对应即出现了乱码的情况。

with open('utf','r',encoding="utf-8") as file:
    print(file.read())

with open('gbk','r',encoding="gbk") as file:
    print(file.read())

输出结果:

小甲
小甲

这样做到编与解两个步骤一一对应对能正常打印出来。
到这里了,是否有了一定的了解了呢?这里只是我们全程进行指定编码格式地去写入数据和读取数据,但是我现在要求你读取一个文件的数据可不知道文件写入数据时的编码格式怎么办呢?重点在后面啦。
判断文件的编码格式
方便检阅,我们这里就用上面的两个文件啦。
安装: pip install chardet
如果是使用Pycharm可以直接在Pycharm里安装

import chardet
with open('utf','rb') as file: #必须用二进制的方式进行读取,否则会报错,因为chardet的探测器是用字节作为标本以给探测器进行检测的
    w = file.read()
    info = chardet.detect(w)
    print(info)

with open('gbk','rb') as file1:
    w = file1.read()
    info = chardet.detect(w)
    print(info)

输出结果:

{'encoding': 'utf-8', 'confidence': 0.7525, 'language': ''}
{'encoding': 'ISO-8859-1', 'confidence': 0.73, 'language': ''}

到这里,是否又有疑惑了,第二个文件的编码格式与我们实际上的不一样了,可这是为什么呢?首先我们先了解一下什么是ISO-8859-1。
维基百科解释得非常好:UTF-8与Latin-1(ISO-8859-1)。前者是可变长度编码,后者是单字节固定长度编码。Latin-1仅编码Unicode字符集的前256个代码点,而UTF-8可用于编码所有代码点。ISO-8859-1 and windows-1252 (Western European languages 西欧文字)
哈哈哈,看完了,没错,和你想的完全一样,这个ISO-8859-1和我们实际上的GBK完全不一样。其实是这样的…
阅读官方文档,**当字节包的长度不够长时,chardet给出的结论是很不可靠的,因为它可能在调用一个不相干的探测器时,该探测器给出了一个超过阈值的可信度,或者两个编码格式刚好有共通的字符,然后就不再往下检测。这么做很容易出现不可靠的检测结果。**所以最好不要检测很少量的字节。同时,在检测开篇有大段其他字符的文档时,最好先手动处理掉无关的符号(不一定会发生错误,因为程序根据初次遍历结果对探测器的顺序做了优先级排序,但是保不准会出错),以求最准确的结果。
为了检测这个chardet模块的真实性,手动改了gbk文件里的内容再次进行上面代码执行并检测。手动改动的内容如下:
在这里插入图片描述
执行结果:
在这里插入图片描述
从结果可以看出,第二文件的编码格式和我们实际上的一致了。结果里的那个“confidence”可以理解为可信度的意思。
所以,这个方法是可以把文件的编码格式判断出来的,就是有时相对于比较少字节内容时就会显得不是很靠谱了。

若有不足之处望留言!

——————END———————
Programmer:柘月十七

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!