python中的解码、编码问题

  python的编码和解码问题烦扰我很久了。虽然平时通过改下编码类型就解决了。但是相关的原理理解的还是不太透彻。今天查阅了众多材料,终于大概明白了。过程中参考了众多优秀的文章,引用的文章链接地址会附在文中或是文后。编码解码这个问题对于没接触过的初学者有点难,因此,也感谢众多前人的记录,这篇文章也将给后来学习者以参考。对了,哪有不对,欢迎指出,共同进步。

  老话说的好:把别人教明白了,自己才算真明白;所以,文章的口吻和用语都偏介绍或是说教学式,在帮助有相同问题的同学看明白的同时,也使自己真的掌握知识点,特此说明。

“问题都是人类自己造成的,计算机没有错。”

  我们以问题为导向,先从报错出发。代码运行过程中我们会遇到各种报错:比如

  于是,我们试着拿关键词谷歌相关错误...但是由于,每个人错误原因不一致,即使错误原因一致,不同版本的python在相关术语和功能上也有不同,于是,到头来,各种混乱。

  但是,也是有收获的,我们发现问题大概分为两种:python的解码编码问题,python的中文解码编码问题;

  ok,于是我们知道,中文的解码编码是个特殊的问题。好。

  我们试着梳理清关键词之间的关系开始;把我们遇到的各种术语先列出来:解码、编码、[ASCII、Unicode、UTF-8、GBK和GB2312]、bit(二进制位)、byte(字节)、

  好,这块知识参考:http://blog.sina.com.cn/s/blog_6d7cf9e50102vo90.html;http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html;http://blog.csdn.net/qq_16234613/article/details/52652968

  看完,我们大体明白,各种为什么需要编码解码以及编码解码标准产生的原因;为什么需要编码解码呢?因为计算机的010101二位进制我们看不懂,我们为了看懂,所以需要解码;又因为,我们要告诉计算机我们干了什么告诉计算机去干什么,就需要计算机明白我们的“语言”,于是需要编码。但是,我们人类的语言各式各样,关于编码解码就出现了各种标准。好的,标准定好了就行了。我们可以指定按照这个标准去干。“但是,总有捣乱的人”,也正是如此,如果世界上所有的事情都是有了标准好好执行就美好了。但是,我们总会不按照标准来,于是就出现了前面所举的错误的情况。

  于是,假如我们清楚了各种标准(当然包括标准的兼容性问题)。我们发现,故事好像越来越清晰了。因为,我们发现,我们只要能够清晰、正确地的告诉计算机怎么按照我们制定的标准好好干活就好了。事实上,也的确如此。

Why—“我们为什么会出错”

 接下来,我们结合python,从你输入字符,到终端显示字符的背后过程,力图弄明白Python中字符编码所涉及的背后逻辑部分。

此部分节选自http://www.crifan.com/summary_python_2_x_common_string_encode_decode_error_reason_and_solution/

  此处就简单介绍一下,在Python中,从你所输入的字符串,到显示出字符串,这背后的过程是什么样的。只有了解了这个大概的过程,和背后的逻辑,你才能真正理解后面的所解释的,字符串编码解码方面的错误,以及如何解决这样的错误。

对于你(人类)

你看到的只是:

  • 你输入了字符串
    • 不论是从Python的IDLE中输入的
    • 还是写入到Python文件中的
  • 然后你去运行的对应代码,该Python代码,经过Python系统,(此处及之后,我称其为Python解析器),的处理
  • 使得你可以看到最终所输出的字符串
    • 不论是在Python的IDLE中看到的
    • 还是在windows的cmd中看到的。

对于Python解析器(机器)

而Python解析器所干的事情,就是:

  1. Python解析器,根据当前的所用的字符串编码类型
    • 此字符串编码类型,是你自己所设置的
      • 不论是在Python的IDLE中,还是Python文件中
      • 都是你自己显示指定对应的编码类型的

       

    • 当然你没显示的指定的话,那就用默认的配置
      • 如果是Python的IDLE,如果你没修改defaultencoding,那么就使用默认的字符编码
        • 可以通过sys.getdefaultencoding()而获得,比如此处获得是:ascii
      • 如果是Python文件,如果你没声明文件编码,则使用默认的编码:UTF-8
        • 常见的做法是指定为对应的UTF-8类型:# -*- coding: utf-8 -*-
          • 相关内容,不了解的可参考:【整理】关于Python脚本开头两行的:#!/usr/bin/python和# -*- coding: utf-8 -*-的作用 – 指定文件编码类型
  2. 去执行你的Python代码
    • 其中,很常见的几种动作是:
      • 打印print对应的所获得的字符
        • 对于字符串打印,Python的逻辑:
          • 如果是Unicode字符串,则可以,自动地,编码为对应的终端所用编码,然后正确的显示出来
            • 比如unicode的字符串,输出到windows的默认编码为GBK的cmd中,则Python可以自动将Unicode编码为GBK,然后输出到cmd中
            • 个别特殊情况,也会出错:
              • 当此unicode字符串中包含某特殊字符,而目标终端的编码集合中,没有此字符,则很明显也是无法实现将Unicode编码为对应的特定编码的字符串,无法正确显示的
          • 如果是某种编码类型的str,则需要该str的编码类型,和目标终端编码匹配
            • 比如GBK的字符串,输出到windows的默认编码为GBK的cmd,则是可以正常输出的
              • 此处后来经过代码测试,就发现一个有趣或者说诡异的问题,虽然我们python文件声明的UTF-8编码,但是实际上实际上是用GBK编码,而此时,文件中的字符串,很明显是用GBK存储的,所以,将此GBK字符,输出到GBK的cmd中,是可以正常输出的。即,此处字符串的类型,很明显只和文件所用的实际编码有关,而和文件所声明的代码无关。
            • 如果是UTF-8的字符串,输出到windows的默认编码为GBK的cmd,就会出错
      • 对相应的字符,进行编码(为某种特定类型的字符str),或解码(为对应的unicode类型的字符)
        • 比如将当前的某种编码的字符串,解码为Unicode字符串
          • 很明显,也是要保证,你字符串本身的编码和所指定的编码,两者之间要一致的
            • 比如:decodedUnicode = someUtf8Str.decode("UTF-8")
            • 而如果用这样的:decodedUnicode = someGbkStr.decode("UTF-8"),那就会出现错误

 

How— “为了避免出错,我们人类应该注意什么”

 

所以你要“守规矩”

  • 确保当前输入的字符串的编码,和对应的编码设置一致,才能使得解析器正常解析你的字符串
  • 确保输出字符串时,所用字符(的编码)和目标输出所用的编码一致,才能正常在输出终端显示
  • 确保字符串本身的编码,和你去解码等操作所用的编码一致,否则很明显会由于编码不一致而无法进行解码(等操作)

 

常见错误简介

很明显,如果你不遵守上述规则,出现前面编码不一致的情况,那么就会出现一些常见的编码解码方面的错误了。因此,开头我们推理出来得什么“中文编码问题”“编码问题”最终归结到守规矩的问题上来了。

此处只是简单举例如下:

  • 你python文件本身是GBK的,对应的字符串也是GBK的,然后你指定按照UTF-8解码为对应的Unicode,那么当然会导致人家Python解析器,按照UTF-8编码的方式,去解析你的,实际上是GBK的字符,当然会出错,无法解析了。
  • 你所要打印的字符,本身是UTF-8的,但是要打印输出的终端是,windows的cmd,其默认编码为GBK,即将UTF-8的字符,显示到GBK编码的cmd中,当然也会出现错误了
  • 你所要打印出来的字符,虽然是unicode,但是其中包含了某些特殊字符,而对应的特殊字符在终端所用的编码中不存在,比如将含某些特殊字符的Unicode字符串,打印到windows的默认编码为GBK的cmd中,而GBK编码集合中,本身就没有这些字符,所以,当然也是无法显示,会出现对应的UnicodeEncodeError的错误的。

 到现在,遵守规则的问题就转化为,具体的编码解码要一直的原则(当然也有兼容的情况,比如)

所以,诸如此类的问题,如果搞懂了之前的逻辑,那么自然很容易理解其错误的根源,并找到解决办法。

即,知其所以然,之后,更容易,知其然。

Examples

结合常见的具体错误,采用“P—A—S”三步法,解决问题

P,【即phenomenon,错误案例(现象)】—>W,【即analysis分析(分析原因)】—>S,【solution(解决问题)】

  •  类型1:Python中,想要将某字符串解码为对应的Unicode,但是所使用的编码类型和字符串本身的编码不匹配
    • 现象:字符串本身,是某种编码类型的字符串,但是结果将其解码为对应的Unicode时,却指定了另外一种编码类型,导致无法正常的解码为对应的Unicode,而出现UnicodeDecodeError之类的错误。
    • 原因:字符串是A编码的(GBK),但是却用B编码(UTF-8)去解码,导致出现UnicodeDecodeError错误;
    • solution:使用和字符串本身编码相同的编码,去解码,就可以正常的解码为Unicode了。
    • 举一反三:如果你以后遇到类似的,UnicodeDecodeError,那么说明是Unicode在解码方面的错误,肯定是将某种编码的字符串,解码为Unicode的过程中,出现的错误。而错误原因,也往往都是编码类型不匹配,然后就可以去检查一下,是不是字符串本身的编码,和你在调用decode时所设置的编码不匹配。
  •  类型2:Python中,打印字符串时,字符串本身的编码,与输出终端中所用编码不匹配
    • 现象:字符串本身,是某种编码的,但是输出,显示,到终端时,终端所使用编码,和你字符串的编码不一样,导致无法正常显示。比较常见的是,本身是UTF-8类型的字符串,但是却将其输出到Windows的cmd中,而cmd中默认是GBK编码的,导致两者不匹配,所以打印字符串时,出现乱码:
    • 原因:把Python文件中的字符串,该Python文件是UTF-8的,所以该字符串也是UTF-8编码的,输出到Windows的cmd中,而cmd中默认编码为GBK,即

      把UTF-8的字符串,在GBK的cmd上显示,则出现了乱码。

    • 解决方案:目的是为了是要输出的字符串的编码,是Unicode,或者和目标输出终端的编码一致,就可以正常输出了。所以可以把UTF-8的字符,解码为对应的Unicode。(也可以进一步的,把Unicode字符串,编码为GBK),然后再输出到GBK的cmd中,就可以正常显示,不是乱码了:

    • 举一反三:以后如果再遇到类似的乱码,先去确定你的字符串本身是什么编码的。再去确定,你所要输出的终端目标中,所用的编码是什么样的。其中,输出的终端,此处举例所用的是,windows的cmd,其他还有可能是输出内容到文件中,其中还涉及文件所用的编码是什么,这些都要自己搞清楚的。总之,确保字符串编码类型,和输出所用的编码类型,两者是一致的,就不会出现乱码了。
  • 类型3:Python中,打印含某些特殊字符的Unicode类型字符串,但是输出终端中字符编码集中不包含这些特殊字符
    • 现象:虽然已经获得了Unicode的字符串了,但是当打印Unicode类型的字符串,到某些终端中时,结果却还是出错了。比如,下面的例子中,就是把Unicode字符串,打印到Windows的cmd中,结果出错。
    • 原因:上述过程中,虽然已经获得了正确的unicode字符串了,但是由于此unicode字符串中包含了一个特殊字符,即那个\u3232,对应的字符,显示出来,像是左右括号中间一个"有"字,即类似于这样的:而此特殊字符,GBK字符集中没有,不存在,所以无法将对应的Unicode字符,编码为对应的GBK字符,所以出现UnicodeEncodeError,更无法打印出来。

      注:对于任何字符,都可以去这里:http://unicodelookup.com/而查到,对应的unicode的值,html中的写法。

      提醒:可以通过输入:0x3232 而查到该特殊字符。

    • 解决办法:则是不同情况,不同处理:

      (1)如果对于这些特殊字符,你不是很关心,即使不显示也无所谓,但是希望剩下的,其他大多数的正常的字符都能显示。

      即,忽略掉特殊字符,显示哪些能显示的字符。(2)如果必须要显示这些字符,或者说必须要保留这些字符。那么本身对于打印这个需求来说,是可以不打印的,因为本身已获得了正常的Unicode字符了。然后剩下的,只是尽量你自己所需要的后续的处理即可。即, 已经得到了正确的unicode字符了,后续该咋办咋办,可以不打印的时候,就不打印,也就不会出错了。

    • 举一反三:如果以后遇到这种,虽然已获得了Unicode字符串,但是还是无法打印等情况,则就要注意去调查一下,是否是由于此处有特殊字符,不存在于输出目标所用字符集中,才导致此问题的。
    • 进一步举一反三:如果你对于上述代码中的encode中的ignore不熟悉,那么你自然应该想到,去了解,去学习这方面的内容。然后通过google搜:unicode encode ignore,就可以找到Python官网的解释了:Unicode HOWTO — Python v2.7.3 documentation而如果你再稍微积极思考的话,就会想到:既然encode有个ignore参数,那是不是还有其他参数?对应的函数原型是啥?

参考:

http://blog.sina.com.cn/s/blog_6d7cf9e50102vo90.html

http://blog.csdn.net/yockie/article/details/46294283

http://blog.csdn.net/qq_16234613/article/details/52652968

http://www.crifan.com/summary_python_2_x_common_string_encode_decode_error_reason_and_solution/

http://www.aikaiyuan.com/8985.html

posted @ 2016-10-16 20:28  coskaka  阅读(224)  评论(0编辑  收藏  举报