有个特别好玩的现象,当咱们为了python编码头疼的时候,几乎搜索到全部的文章都会先发一通牢骚。而后在迫不得已地写解决思路(是解决思路不是方案)。这个问题真不是新手问题,即便是十几年python老手也常常头疼。中国外国都同样。看看这个python专家在 PyCon大会上用半个多小时讲解乱码的视频就了解了,他本身都给本身的来回encoding, decoding, encoding, decoding说晕了,台下举手他都拒绝回答,可想而知这个问题复杂性。
我认为,几乎每一个pythoner,都会有一段人生浪费在了编码上。能够说这个问题,是若是你不不折不扣解决,就永远会崩溃的地步。翻看我曾经写的数篇文章就知道了:html
牢骚结束,下面是我又一次用了两个成天才测试整理书写完成的ipython notebook笔记。ipynb
格式的笔记源文件在这里,固然有可能会连接失效,有喜欢ipython的live coding笔记的且想要用这个笔记测试编码的,请联系我。python
print大法
若是python的print的特性都没有了解的话,但愿你不要贸然尝试用print去调试测试乱码编码的问题。
这里的print厉害到让你不高兴的地步——它无论你塞过来的是什么格式什么编码,字符串数组对象什么的的都一口气全打印出来。
感受好像很好,但实际上是咱们仔细研究编码问题的最大阻碍。
由于你塞给print一个unicode它能打出中文,塞一个utf-8或iso8895给它,也同样给你打印出原文。这样以来,你看着它出现原文后,就欣喜若狂产生了一种胜利的错觉。
因此我想在这里最早说清楚它:git
也不是说这种时候一点都不能用,而是说你能够print别的什么东西,可是若是想看清某个变量本质的话,千万不要用。
这个时候要用print repr(字符串)
,或者最好是在命令行或ipython里面测试,像这样:github
看出区别了吗?明确了这点,再来继续研究编码问题。web
unicode
和str
若是type(字符串)
显示结果是str
,其实指的是bytes
字节码。
而其它各类咱们所说的utf-8
,gb2312
等等也都是Unicode的不一样实现方式。
这里不要去考虑那么复杂,只要先记住这两大阵营就行。json
encoding
和decoding
绝对要记住的:
从unicode
转换到str
,这个叫encoding
,编码。
从str
转换到unicode
,这个叫decoding
,解码。
(图片引用自知乎相关某答案。)数组
来回记住这个问题,才能进入下一步!网络
而后来看个案例。测试
经过上面两种格式的对比咱们看到,str和unicode的各类区别。
那么,既然变量里面会出现两种不一样的格式,若是咱们把两种格式的字符串连在一块儿操做会发生什么呢?
以下:编码
UnicodeDecodeError: 'ascii' codec can't decode
就这样出现了!以上是咱们用显性
字符串来比较两种格式字符串的区别。
可是,咱们常常性处理python编码问题,都不是在这种显性
的字符串上出现的,不是从网上爬取的就是从本地文件读取的,意思就是文件内容庞大,编码格式很难猜到是什么。
因此这里咱们将问题再拆分为两部分讨论:本地文件和网络资源。
首先在本地创建一个有中文的以utf-8
格式保存的文本文件(实际上不管.txt仍是.md等都无所谓,内容是同样的)。
内容只有'你好'。
上面看到,从文件读取出来的,就是str格式的字符串。
那么若是要把str转化为unicode,就要解码,也就是decoding.
因此上面提到,必需要记住这两个区别。
那么若是如今我搞反了怎么办?就会再次出现下面错误:
为了不两种格式的字符串在一块儿乱搞,统一他们是必须的。可是以哪种为统一的呢,unicode仍是bytes?
网上各类文章统一口径,要求代码中出现全部的变量都统一为unicode。
但是我在实践和测试中都愈来愈发现:这种作法真的不那么可靠,甚至我怀疑有可能咱们碰到那么多的问题,都是由它搅乱引发的。
这样就明白了:除了r.text返回的内容外,其它几乎都是使用str格式,也就是bytes字节码码。因此咱们只要转化requests相关的内容就行!
实际上,requests返回的response中, 除了用.text
获取内容,咱们还能够用.content
获取一样的内容,只不过是bytes格式。
那就正和咱们意,不用再去转化每个地方的字符串,而只要盯紧这一个地方就足够了。
先提醒下,变成unicode的过程,叫decoding
。不要记错。
像.text
常常把ISO8859
等猜不到也检测不到编码(机率很低)的字符串扔过来,若是遇到的话,是很麻烦的。decoding
有两种方法:
unicode(b'你好‘) b'你好'.decode('utf-8')
这里由于不知道来源的编码,因此必须用unicode()
来解码,而不能用.decode('utf-8')
,由于显然你不能乱写解码名称,若是来源果然是(很大概率是)ISO8859
等方式,那么错误的解码确定会产生乱码,或者直接程序报错。切记!
因此这里只能用unicode()
解码。以下例:
str
格式字符串只要控制好外来源的字符串,统一为str
,其它一切都好说!
实际上,我发现遇到的绝大多数编码问题,实际上不是python原生方法致使的,而是这些外来库所引发的!由于每一个模块都会有本身的一套处理编码的方式,你还真不知道它是采用哪一个。就像JSON的dumps()同样埋着大坑等着咱们。因此真正应该盯紧的就是这些库了。
下面是一个从获取网络资源(含中文且被requests认为编码是ISO8850的网页)到本地操做且存储到本地文件的完整测试。
import requests r = requests.get('http://pycoders-weekly-chinese.readthedocs.io/en/latest/issue5/unipain.html') # write a webpage to local file with open('test.html', 'w') as f: f.write( r.content ) # read from a local html file with open('test.html', 'r') as f: ss = f.read()
大功告成!效果以下:
另外,关于JSON的乱码问题,又是一个新的较长篇章。我会单分一篇,请到个人专栏里找。