Python编码问题

python的编码有点繁琐,在这里总结一下。

一、str与unicode

    python中有两种数据模型来支持字符串这种数据类型,str和unicode。它们的基类都是basestring。比如s = "中文"就是str类型的字符串,而u=u"中文"就是一个unicode类型的字符串。

    严格来说,str应该叫做字节串,它是unicode经过编码后的字节组成的序列。unicode是由str类型的字符串解码后得到,同时unicode也可以编码成str类型。

二、文件编码与头部编码声明

        python默认源代码文件是ascii编码,所以一旦出现不在ascii以外的字符(比如说中文字符)就会出现问题。所以如果在源代码中出现非ascii字符,需要在文件头部声明字符编码,声明凡是如下:

  # -*- coding: encoding -*-

        注意:这里声明的编码方式与最后文件存储的编码方式要一致!要一致!要一致!。

        (也就是说,头部的声明只是告诉python使用了encoding编码,至于最后编辑器保存的编码方式并不一定就是encoding。正确的做法是保证他们一致!正确的做法是保证他们一致正确的做法是保证他们一致!)

 原因:文件头部编码声明决定了python解析源码中的str的编码选择方式。比如头部声明的是utf-8编码,则代码中s="中文",python就会按照utf-8编码格式来解析。如果文件本身编码是gbk,而源码文件头部声明的编码是utf-8,这样如果源码中有中文就会有问题了,因为本身中文str存储是按照gbk编码来的,而python在解析str的时候又以为是utf-8编码,这样就会报SyntaxError: (unicode error) 'utf8' codec can't decode byte错误。

三、读写文件编码

        A)采用python的open()方法打开文件时。

        调用read()读取的是str,编码就是文件本身的编码。此时我们需要调用decode('文件本身编码')函数,将其从其本身的编码解码成unicode编码。

       调用write()写文件,

            1)如果参数是unicode编码字符串,则需要用指定编码encode该字符串,而不能直接write unicode编码字符串;

            2)如果write()参数是其他编码格式的str(不同于待写入文件编码),则需要先用该str的编码进行decode()成unicode编码字符串,然后再对该unicode字符串encode()成目标编码(待写入文件编码),然后在写                     入。

# -*- coding: utf-8 -*-

#头部声明的为utf-8
str_utf8 = '哈哈'

#unicode编码字符串
str_unicode = u'哈哈'

f = open('./gbktext.txt','w')

#将unicode成gbk
f.write(str_unicode.encode('gbk'))

#f.write(str_unicode)出错

#将utf-8解码成unicode,再编码成gbk
f.write(str_utf8.decode('utf-8').encode('gbk'))

f.close()

        B)为了保证编码正确常使用codecs模块,可以指定编码打开文件。

         调用read()读取的是unicode。

         调用write()写文件

               1)如果write()参数是unicode,则使用打开文件时的编码写入,

               2)如果write()参数是其他编码格式的str(不同于待写入文件编码),则需要先用该str的编码进行decode()成unicode编码字符串,然后在写入。

# -*- coding: utf-8 -*-
import codecs

def testCodecs():
    unicode_str = u"中文"
    #encode from unicode to utf-8
    utf8_str = unicode_str.encode('utf-8')
    #encode from unicode to gbk
    gbk_str = unicode_str.encode('gbk')
    
    filename = 'utf8_text.txt'
  
    wfp = codecs.open(filename, 'w', 'utf-8')
    
    #unicode字符串
    wfp.write(unicode_str)
    
    #先decode()成unicode
    wfp.write(gbk_str.decode('gbk'))
    
    wfp.flush()
    wfp.close() 
    
    #read file in utf-8
    rfp = codecs.open(filename, 'r','utf-8')
    str_type = rfp.read()
    
    #读出的类型是unicode ==> type(str_type) =  <type 'unicode'>  
    print 'type(str_type) = ',type(str_type)
    
if __name__ == "__main__":
    testCodecs()

四、其他注意事项

        1、windows控制台中文编码是GBK,Python的IDLE编码也是GBK

    234811_P1z3_1446623.jpg

    095038_d5Eu_1446623.png

        但这里为什么不是中文字符"哈哈哈"而是'\xb9\xfe\xb9\xfe\xb9\xfe',其实这里输出的是“哈哈哈”的GBK编码的16进制形式(他是正确的)。为什么print的时候确实是"哈哈哈"?

    When Python executes a print statement, it simply passes the output to the operating system (using fwrite() or something like it), and some other program is responsible for actually displaying that output on the screen.

    To print data reliably, you must know the encoding that this display program expects.

    也就是说当调用print的时候它仅仅是将字符串传给了操作系统,至于如何显示是由其他的program负责的(比如windows终端,IDLE),负责显示的program会对传入的字符串进行编码。所以要想获得可靠的输出,必须要知道负责输出的program的编码(并与负责显示的program的编码一致)。

    print的时候显示是由python IDLE负责,也就是按GBK解释,打印自然是"哈哈哈"

    将其decode()的时候可以得到正确的unicode字符串 u'\u54c8\u54c8\u54c8',当print unicode字符串的时候,自动以GBK(负责显示的program所使用的编码) encode()然后显示,所以打印也是"哈哈哈"。

    也就是说要想获得正确的显示,必须保证print的字符串编码格式与终端(负责显示的终端编码格式)一致!

    但是,如果要显示的字符串中包含有负责显示program(如windows终端)所使用编码不会包含的字符会怎样?

    对,会出错。比如,当此unicode字符串中包含某特殊字符,而目标终端的编码集合中没有此字符,则很明显也是无法实现将Unicode编码为对应的特定编码的字符串,无法正确显示的。

# -*- coding: utf-8 -*-

slashUStr = "\\u3232\\u6674"; #(有) 晴

decodedUniChars = slashUStr.decode("unicode-escape");
#如果没有打印的刚性需求的话,我们完全可以选择不打印,直接进行下一步的处理
#因为此处已经可以正常获得对应的两个Unicode字符了,完全可以继续进行后续处理
unicodeButContainSpecialChar = decodedUniChars;

print "unicodeButContainSpecialChar=",unicodeButContainSpecialChar;

    他在Python IDLE中其实显示是正确的(原因不明,先忽略)。如果在windows 终端显示,会按预期的出错(#UnicodeEncodeError: 'gbk' codec can't encode character u'\u3232' in position 0: illegal multibyte sequence),因为他包含GBK字符集不包含的字符,
 Unicode字符:0x3232,是个特殊字符,而此字符,不在GBK编码字符集。

    但是还有一种办法可以解决。  

s.encode("gbk", "ignore");
#忽略掉无法正确编码的字符

#假设s试试utf-8编码,按gbk解码肯定有问题,若非要解码
s.decode('gbk','ignore')
#忽略掉无法正确解码的字符

2、windows记事本问题

        在window下面用记事本编辑文件的时候,如果保存为UNICODE或UTF-8,分别会在文件的开头加上两个字节“\xFF\xFE”和三个字节“\xEF\xBB\xBF”。在读取的时候就可能会遇到问题。(windows控制台,python shell下有问题,pythonIDLE正常)

    001309_bUVc_1446623.jpg

# -*- coding: utf-8 -*-

f = open('./utf8.txt','r').read()

print f

print f.decode('utf-8')[1:]

    002157_Rh1j_1446623.jpg

   五、参考

        http://www.cnblogs.com/huxi/archive/2010/12/05/1897271.html

        http://www.crifan.com/python_already_got_correct_encoding_string_but_seems_print_messy_code/

        

        

转载于:https://my.oschina.net/v5871314/blog/613187

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值