资讯专栏INFORMATION COLUMN

关于解决Python乱码问题的终极解决方案 (TL;DR)

Lemon_95 / 1004人阅读

摘要:关于解决乱码问题的终极解决方案有个特别好玩的现象,当我们为了编码头疼的时候,几乎搜索到所有的文章都会先发一通牢骚。另外,关于的乱码问题,又是一个新的较长篇章。

关于解决Python乱码问题的终极解决方案 (TL;DR)

有个特别好玩的现象,当我们为了python编码头疼的时候,几乎搜索到所有的文章都会先发一通牢骚。然后在无可奈何地写解决思路(是解决思路不是方案)。这个问题真不是新手问题,即使是十几年python老手也经常头疼。中国外国都一样。看看这个python专家在PyCon大会上用半个多小时讲解乱码的视频就了解了,他自己都给自己的来回encoding, decoding, encoding, decoding说晕了,台下举手他都拒绝回答,可想而知这个问题复杂性。

我认为,几乎每个pythoner,都会有一段人生浪费在了编码上。可以说这个问题,是如果你不彻彻底底解决,就永远会崩溃的地步。翻看我曾经写的数篇文章就知道了:

对Python 2.x的通宵抱怨

Python中文字符的理解:str()、repr()、print

Python里中文编码的理解:unicode、utf-8、gbk

牢骚结束,下面是我又一次用了两个整天才测试整理书写完成的ipython notebook笔记。ipynb格式的笔记源文件在这里,当然有可能会链接失效,有喜欢ipython的live coding笔记的且想要用这个笔记测试编码的,请联系我。

首先,需要先要了解python的print大法

如果python的print的特性都没有了解的话,希望你不要贸然尝试用print去调试测试乱码编码的问题。
这里的print厉害到让你不高兴的地步——它不管你塞过来的是什么格式什么编码,字符串数组对象什么的的都一口气全打印出来。
感觉好像很好,但其实是我们仔细研究编码问题的最大阻碍。
因为你塞给print一个unicode它能打出中文,塞一个utf-8或iso8895给它,也一样给你打印出原文。这样以来,你看着它出现原文后,就欣喜若狂产生了一种胜利的错觉。
所以我想在这里最先说清楚它:

不要轻易在研究乱码的时候用print测试目标!

也不是说这种时候一点都不能用,而是说你可以print别的什么东西,但是如果想看清某个变量本质的话,千万不要用。
这个时候要用print repr(字符串) ,或者最好是在命令行或ipython里面测试,像这样:

看出区别了吗?明确了这点,再来继续研究编码问题。

简单来说,先要记住,在Python2里字符串只有两大阵营: unicodestr

如果type(字符串)显示结果是str,其实指的是bytes字节码。
而其它各种我们所说的utf-8gb2312等等也都是Unicode的不同实现方式。
这里不要去考虑那么复杂,只要先记住这两大阵营就行。

encodingdecoding

绝对要记住的:
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呢?

先提醒下,变成unicode的过程,叫decoding。不要记错。
.text经常把ISO8859等猜不到也检测不到编码(机率很低)的字符串扔过来,如果遇到的话,是很麻烦的。
decoding有两种方法:

unicode(b"你好‘)
b"你好".decode("utf-8")

这里因为不知道来源的编码,所以必须用unicode()来解码,而不能用.decode("utf-8"),因为显然你不能乱写解码名称,如果来源果真是(很大几率是)ISO8859等方式,那么错误的解码肯定会产生乱码,或者直接程序报错。切记!

所以这里只能用unicode()解码。如下例:

结论:一定记住,全文都统一用str格式字符串 只要盯紧requests、json等这种经常处理外来资源的库就好了。

只要控制好外来源的字符串,统一为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的乱码问题,又是一个新的较长篇章。我会单分一篇,请到我的专栏里找。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/41379.html

相关文章

  • JavaWeb 乱码问题终极解决方案

    摘要:经常有读者在公众号上问乱码的问题,昨天又有一个小伙伴问及此事,其实这个问题很简单,但是想要说清楚却并不容易,因为每个人乱码的原因都不一样,给每位小伙伴都把乱码的原因讲一遍也挺费时间的,因此,松哥今天决定写一篇文章,和大伙好好捋捋中的乱码问题 经常有读者在公众号上问 JavaWeb 乱码的问题,昨天又有一个小伙伴问及此事,其实这个问题很简单,但是想要说清楚却并不容易,因为每个人乱码的原因...

    canger 评论0 收藏0
  • 一文帮你发现各种出色GAN变体

    摘要:也是相关的,因为它们已经成为实现和使用的主要基准之一。在本文发表之后不久,和中有容易获得的不同实现用于测试你所能想到的任何数据集。在这篇文章中,作者提出了对训练的不同增强方案。在这种情况下,鉴别器仅用于指出哪些是值得匹配的统计信息。 本文不涉及的内容首先,你不会在本文中发现:复杂的技术说明代码(尽管有为那些感兴趣的人留的代码链接)详尽的研究清单(点击这里进行查看 链接:http://suo....

    qpal 评论0 收藏0
  • Python 性能快速优化: 系列二

    摘要:注本文的原文地址为这个是性能快速优化的第二部分,第一部分请点击这里。当你写代码了,过一遍数据结构,迭代结构,内建和为创建扩展,如有必要。更新是在的范围之外,这意味着你可以使用这个标准库来运行多个进程。看下我的的印度演讲 注:本文的原文地址为 Quick Python Performance Optimization: Part II 这个是 Python 性能快速优化的第二部...

    yexiaobai 评论0 收藏0
  • Promise--一诺千金

    摘要:立即执行的验证的参数是异步调用的验证当在中通过调用使状态稳定之后,不管调用多少次,值都不会再发生变化一诺千金的优点是什么我看了很多文章在介绍的时候都会提到回调噩梦。 showImg(https://segmentfault.com/img/remote/1460000006797059); TL;DR本文是对 Promise 一些概念点的归纳和对 Promise 在解决什么问题的思考。...

    xiongzenghui 评论0 收藏0
  • Mysql在debian系统中不能插入中文终极解决方案

    摘要:在环境下,彻底解决无法插入和显示中文的问题下插入中文显示乱码解决方案回车输入密码进入查看状态如下默认的是客户端和服务器都用了,所以会乱码。解决方案上文提到了用临时方法更改数据库的字符集设置,显示中文,但是后来发现在有的系统下并不能成功。在debian环境下,彻底解决mysql无法插入和显示中文的问题Linux下Mysql插入中文显示乱码解决方案mysql -uroot -p 回车输入密码进入...

    Tecode 评论0 收藏0

发表评论

0条评论

Lemon_95

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<