在python编程的过程中,编码和解码是经常遇见的问题。

常见的编码有unicode,gb2312,gbk,utf-8等等。

在编码和解码的过程过,使用最频繁的函数就是decode()和encode。

其中

   decode()函数,表示把字符串从原有编码,解码成unicode中间编码。

   encode()函数,表示把字符串从unicode中间编码,转换成目标编码。

 

举例:

lines = open(input_file_name, 'r').readlines()  
for line in lines:  
    line = line.strip()  
    line = line.decode('gbk')   # 从原有的gbk编码,转换成unicode中间编码  
    line = line.encode('utf-8') # 从unicode中间编码,转换成UTF-8编码  
    output_file.write(line + '\n')

 

在使用decode()或者encode()函数的过程当中,经常会碰到诸如这样的问题:

'gbk' codec can't decode byte 0xc1 in position 8687: incomplete multibyte sequence

表示在编码或者解码的过程当中,碰到无法编解码的字符。

如果你不希望被这样的错误中断程序的运行,可以加上一个忽略这类错误的参数ignore.

line = line.decode('gbk', 'ignore')  
line = line.encode('utf-8', 'ignore')

 

表示忽略掉无法转换的字符。

# -*- coding: utf-8 -*-
# 直接保存为Python脚本,对照执行结果会好看点。
# 实验的内容都是在Python 2.7.x下进行的。
# Python3默认采用unicode来处理内部编码问题,所以不适用本内容。

#########################
#  Python中的字符类型   #
#########################
# Python中的字符类型分两种:
# 1. str类型:ascii表中的字符,占一个字节,所以也叫字节字符。字面量用双引号表示。
# 2. unicode类型:一个字符串占用的字节数由保存时采用的编码格式相关。字面量用带“u”前缀的双引号表示。
s = 'OK, '
u = u'我, '
u1 = u'我'
u2 = u'爱Python'
print 's:', s
print 'u1:', u1
print 'u2:', u2

# 解析器通常把unicode字符转换成Unicode转义序列
# 转义序列以"\u"开头
print 'repr(s): ', repr(s)
print 'repr(u1): ', repr(u1)
print 'repr(u2): ', repr(u2)

# 这种转义序列只在unicode字面量中有效
print "'\u6211': ", '\u6211'
print "u'\u6211': ", u'\u6211'
print ''

# 也可以用str()函数创建str字符串,用unicode()函数创建unicode字符串
print 'type of str(s): ', type(str(s))
print 'type of unicode(s): ', type(unicode(s))
# 可以给unicode()函数传入一个unicode字符串
print 'type of unicode(u): ', type(unicode(u))
# 但是如果我们给unicode()函数传入'我',又会怎样呢?
try:
    print "unicode('我'):", unicode('我')
except UnicodeDecodeError as e:
    # 错误信息
    # 解析器试图用ascii编码来解码我们传入的参数,原因会在下面将到
    print e

#########################
#       编码和解码        #
#########################
# 编码的过程其实就是采用一定的编码格式将unicode字符转换成str字符的过程
# 非ascii码字符按字节为单位被编码成十六进制转义字符
# 解码采用的编码格式跟设置和环境有关
utf8_s = s.encode('utf-8')
utf8_u = u.encode('utf-8')
utf8_u1 = u1.encode('utf-8')
utf8_u2 = u2.encode('utf-8')
print 'utf8_s: ', s
print 'repr utf8_u: ', repr(s)
print 'utf8_u: ', u
print 'repr utf8_u: ', repr(utf8_u)
print 'utf8_u1: ', utf8_u1
print 'repr utf8_u1: ', repr(utf8_u1)
print 'utf8_u2:', utf8_u2
print 'repr utf8_u2: ', repr(utf8_u2)
# 如果我们的str字面量中有非ascii码字符,解析器会自动对其进行编码
print "'我爱Python': ", '我爱Python'
print "repr '我爱Python': ", repr('我爱Python')

# 来看看上面碰到的问题,我们将带‘我’(str类型)传给unicode函数,结果报错了
try:
    print "unicode('我'):", unicode('我')
except UnicodeDecodeError as e:
    # 发生错误了,解析器试图用ascii编码来解码我们传入的参数
    print e
# 原因就是解析器会先将参数用默认的编码格式(这里是utf-8)进行编码,然后传给unicode()函数,
# unicode函数的帮助信息,其中有段是这么说的:
'''unicode(string[, encoding[, errors]]) -> unicode object
 |  
 |  Create a new Unicode object from the given encoded string.
 |  encoding defaults to the current default string encoding.
'''
# unicode类总会用第二个参数指定的编码格式来解码第一个参数,如果第二个参数为空,就采用默认的格式。
# 脚本开头指定了utf-8编码格式,因此这里传入的‘我’被自动采用utf-8进行编码。
# 可是这里unicode并没有采用我们开头指定的utf-8格式来解码,而是ascii码,那当然会报错。
# 为什么会采用ascii码,我估计原因是这样的,Python 2.7.x在解析器内都是默认采用ascii作为默认编码格式的,
# 而我们在文件开头指定的utf-8格式只对本文件中的字符串字面量有效,而unicode类是定义在其他的模块文件里。

# 我们在文件开头用了“coding: utf-8”指定了编码格式,
# 因此解析器会采用这个格式去解码本文件中碰到的编码字符串
# 如果这个编码字符串不是用utf-8格式编码的,就会出错
# 用print输出的时候,会忽略这个错误,打印空白
print 'gbk_u1: ', u1.encode('gbk')
print 'repr gbk_u1', repr(u1.encode('gbk'))
try:
    # 但如果用decode()函数去解码不正确的编码字符串就会报错
    print u1.encode('gbk').decode('utf-8')
except UnicodeDecodeError as e:
    print e
print ''

#########################
#    更多关于编码字符串    #
#########################
# 对解析器来说,编码字符串没什么特别的,就是一个str类型的字符串
# 可以这么理解,unicode字符串经过编码后得到了一个str类型的字符串
print 'type of utf8_u1: ', type(utf8_u1)
# 和普通str字符串一样,解析器会用默认的编码来解码str字符串中的十六进制转义字符
print r"'\xe7\x88\xb1Python': ", '\xe7\x88\xb1Python'
# 你也可以指定编码格式
print r"'\xe7\x88\xb1Python'", '\xe7\x88\xb1Python'.decode('utf-8')
# str字符串通过解码后得到是一个unicode字符串,哪怕是用ascii来解码
print "type of decoded 'a': ", 'a'.decode('utf-8')
# 我们想要在str字符串中包含“\xe7”这样的字符就必须要对“\”转义
print r"'\\xe7\\x88\\xb1Python': ", '\\xe7\\x88\\xb1Python'
# 如果unicode字符串中包含"\xe7"字样又会怎么样呢?
print r"u'\xe7\x88\xb1Python': ", u'\xe7\x88\xb1Python'

# 编码字符串中的十六进制转义字符表示一个字节,ascii码字符也是一个字节,
# 因此我们可以通过用len函数计算字符串编码后的编码字符串的长度的方式来得到字符串占用的字节数
print 'length of utf8_u1: ', len(utf8_u1)
print ''

#########################
#       字符串相连       #
#########################
# str字符串可以相连
print 's + s: ', s + s
# unicode字符串也可以直接相连
print 'u + u1 + u2: ', u + u1 + u2
# 编码字符串相连时,先连接,输出时再一起解码
print 'utf8_u1 + utf8_u2: ', utf8_u1 + utf8_u2
try:
    # unicode字符串和编码字符串相连的时候,系统会先用一种编码格式去解码编码字符串,
    # 如果不能正确解码编码字符串,就会报错
    print 'u + utf8_u1 + utf8_u2: ', \
          u + utf8_u1 + utf8_u2
except UnicodeDecodeError as e:
    print e

# 但是用str字符与编码字符串相连是不会报错的
# 因为str字符的编码字符跟本身是一样的,直接加到编码字符串中不会破坏原编码字符串
# 系统会先将str字符加到编码字符串中,然后一起解码
print 's + utf8_u1 + utf8_u2: ', \
          s + utf8_u1 + utf8_u2

# 将编码字符串先解码再与unicode字符相连,就不会报错
print 'decoded s + utf8_u1 + utf8_u2: ', \
          s + utf8_u1.decode('utf-8') + utf8_u2.decode('utf-8')

# 不同编码格式的编码字符串相连,是不会报错的(都是str类型嘛),但是相连后的结果就乱掉了,系统没办法解码了
print 'repr gbk_u1 + utf8_u2: ', repr(u1.encode('gbk') + u2.encode('utf-8'))
print 'gbk_u1 + utf8_u2: ', u1.encode('gbk') + u2.encode('utf-8')

# 总结下字符串相连,要点就是:
# 同类型的直接相连;不同类型,先对unicode类型进行编码(得到str类型的编码字符串),再相连。
print ''

#########################
#       文件的编码       #
#########################
u = u'我爱Python'
print u
print repr(u)

# 先创建一个示例文件
w = open('demo.txt', 'w')
try:
    # 直接传入一个unicode字符串,可能会报错
    w.write(u)
    w.close()
except UnicodeEncodeError as e:
    # 错误原因是write()方法只接受str类型的字符串
    # 解析器会采用ascii编码去编码unicode字符
    print e
print ''

# 因此我们需要先编码再写入
w.write(u.encode('utf-8'))
w.close()

# 我们再把它读取出来
r = open('demo.txt', 'r')
content = r.read()
r.close()

# 通过repr和type检查,我们可以看到读出来的还是str类型的编码字符串
# 而没有被转换成unicde字符串
print 'repr content: ', repr(content)
print 'type of content: ', type(content)
print 'content: ', content

# 总结下:
# open()函数以字节为单位,从磁盘读取数据,得到str类型的文本,
# 如果某个字节不是asccii码字符,就用转义的十六进制来表示(其实也就是我们所说的编码字符串)。
# 文本在得到的file对象中都是以这种格式的str字符来处理的。这种方式规避来无法预知的编码问题,而把编码解码
# 的问题抛给调用者自己来解决。
# 因此,我们在处理从文件中读取的文本和程序中其他文本的时候要注意这点,始终记住:
# 同类型的直接相连;不同类型,先对unicode类型进行编码(得到str类型的编码字符串),再相连。

# 那么我们如果要直接处理uncode字符串怎么办?
# 用codecs模块

 

 

参考推荐

Python 字符编码与解码:unicode、str、中文

Python utf-8和utf8的区别

Python中Base64编码和解码

Python 中正确使用 Unicode

Python 中文包含判断及unicode

Python中解决UnicodeDecodeError: ‘ascii’ codec can’t decode byte 0xe5 in position 1: ordinal not in range(128)