python3编码问题终结者--还搞不懂你来找我

python
unicode
bytes
str
编码

首先需要说明一下,该篇文章是以python3为基础的,python2是否适合没有验证过。
由于python编码问题确实比较多,文章篇幅可能较长,请耐心看完,绝对物超所值,何况还是免费的,只求转载的时候注明出处,谢谢!

一、 简单的编码介绍

平常我们可能听说过很多编码格式,如 ASCII码,Unicode,utf-8,gbk等等。为了不让文章臃肿,所以在这不再赘述,如想了解,请跳转到这个链接。
各种字符编码介绍

但是py3里,只有 unicode编码格式 的字节串才能叫作str。
其他编码格式的统统都叫bytes,如:gbk,utf-8,gb2312…………

在py3中,Unicode编码就像是一个枢纽,例如gbk的格式要想转化成utf-8,那么必须先转化成Unicode,然后再从Unicode转化成utf-8。

python3编码问题终结者--还搞不懂你来找我 - marsggbo - 火星教教主-marsggbo
 

编码格式转换.png

二、老大难的文件操作

python各种各样的扩展卡确实给我们带来了极大的方便,但是对于初学者而言,编码问题却一而再再而三的出现,尤其以文件操作最为明显(反正我是这样的)
接下来将主要介绍文件读写操作和文件编码方式检测的方法。

  • 文件读写:open还是 codecs.open?

python读写文件估计大家都用open内置函数,但是用open方法打开会有一些问题。open打开文件只能写入str类型,不管字符串是什么编码方式。例如

  1. >>> fr = open('test.txt','a'
  2. >>> line1 = "我爱祖国" 
  3. >>> fr.write(line1) 

这样是完全可以的。但是有时候我们爬虫或者其他方式得到一些数据写入文件时会有编码不统一的问题,所以就一般都统一转换为unicode。此时写入open方式打开的文件就有问题了。例如

  1.  
  2. >>> line2 = u'我爱祖国' 
  3. >>> fr.write(line2) 
  4.  
  5. Traceback (most recent call last): 
  6. File "<pyshell#4>", line 1, in <module> 
  7. fr.write(line2) 
  8. UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-11: ordinal not in range(128) 
  9. >>> 

怎么办,我们可以将上面的line2编码成str类型,但是太麻烦。我们要把得到的东西先decode为unicode再encode为str。。。
input文件(gbk, utf-8...) ----decode-----> unicode -------encode------> output文件(gbk, utf-8...)

代替这繁琐的操作就是codecs.open,例如

  1. >>> import codecs 
  2. >>> fw = codecs.open('test1.txt','a','utf-8'
  3. >>> fw.write(line2) 
  4. >>> 

不会报错,说明写入成功。这种方法可以指定一个编码打开文件,使用这个方法打开的文件读取返回的将是unicode。写入时,如果参数 是unicode,则使用open()时指定的编码进行编码后写入;如果是str,则先根据源代码文件声明的字符编码,解码成unicode后再进行前述 操作。相对内置的open()来说,这个方法比较不容易在编码上出现问题。

  • 还是文件读写操作

上文中介绍的codecs.open()方法虽然明显比open要方便好用很多,但是使用这个函数的前提是我们需要知道文件的编码方式,但是事实是我们大多数情况下不知道文件的编码方式,所以一下给出两种解决办法。

  • 1.最原始的方法。。。

  1. ways = ["utf-8","gbk","gb2312","ASCII","Unicode"
  2. for encoding in ways: 
  3. print(encoding) 
  4. try
  5. with codecs.open("test.csv","r",encoding=encoding) as f: 
  6. data = f.read() 
  7. print(type(data)) 
  8. break 
  9. except Exception as e: 
  10. pass 

将python中常用的编码方式用list表示,然后用for循环逐一带入检验。由于utf-8和gbk用的较多,所以建议放在list的最前面。
一旦文件操作成功,则break,跳出循环。

  • 2.比较高端的方法

可以以bytes的形式对文件进行操作,这样即使不知道文件的编码方式也同样可以进行读写操作了,但是在最后需要进行decode或者encode。
如果对decode和encode不了解,请阅读这篇文章python编码问题之"encode"&"decode"

  1. with codecs.open("test.csv","rb") as f: 
  2. data = f.read() 
  3. print(type(data)) 
  4. encodeInfo = chardet.detect(data) 
  5. print(data.decode(encodeInfo["encoding"])) 

亲测得到的结果如下图

python3编码问题终结者--还搞不懂你来找我 - marsggbo - 火星教教主-marsggbo
 


4.png

解释一下上面的代码中的chardet.detect()
chardet是一个python3自带的库,用于检测文本的编码方式,他会返回一个字典,格式是{"encoding" : "xxx", "confidence" :" xxx"}但是注意它一般只能检测bytes类型的编码格式,比如

  1. import chardet  
  2.  
  3. a = "你好" 
  4. print(type(a)) 
  5. chardet.detect(a) 
  6.  
  7. >>> 
  8. <class 'str'> 
  9. Traceback (most recent call last): 
  10. File "E:\Code\python\Flask\csv-mysql\test.py", line 63, in <module> 
  11. chardet.detect(a) 
  12. File "C:\Python35\lib\site-packages\chardet\__init__.py", line 25, in detect 
  13. raise ValueError('Expected a bytes object, not a unicode object'
  14. ValueError: Expected a bytes object, not a unicode object 

总结一下:
本文一方面简单介绍了python的编码情况,另一方面介绍了文件操作中会遇到了问题以及解决的办法。

提出的建议是

  • 使用codecs.open()打开文件

  • 使用bytes方式访问文件,如rb和wb

  • 使用chardet.detect()检测bytes类型文本的编码格式,然后再解码(decode)或者编码(encode)


End


posted @ 2017-03-26 11:27  marsggbo  阅读(205)  评论(1编辑  收藏  举报