python编码解码梳理

        最近小白写python爬虫,又遇到编码问题,真的难受!!!之前遇到编码问题照着网上最后也能解决,不过每次写完感觉对编码转换还不是很明白。为此想起了自己之前买的一本《流畅的Python》,里面就有一章是讲述文本和字节序列,看了一小节顿时茅塞顿开。因此重新梳理了一遍自己对编码的理解,如有错误,请告诉我,谢谢~!本文部分内容来自这本书。


lation1
    (即iso8859_1) 一种重要的编码,是其他编码的基础。
Windows-1252 或 CP-1252
    microsoft指定的latin1超集,添加了有用的符号,例如弯引号和欧元
cp437
    BM pc最初的字符集,包含框图符号。与后来出现的latin1不兼容。 

环境是windows 10,python3.6.1

处理UnicodeEncodeError
>>> s = 'Montr�al'
>>> s.encode('utf8')                    一
b'Montr\xef\xbf\xbdal'
>>> s.encode('utf-16')
b'\xff\xfeM\x00o\x00n\x00t\x00r\x00\xfd\xffa\x00l\x00'
>>> s.encode('cp1252')                  二
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "D:\Users\Rosefinch\AppData\Local\Programs\Python\Python36-32\lib\encodings\cp125
2.py", line 12, in encode
    return codecs.charmap_encode(input,errors,encoding_table)
UnicodeEncodeError: 'charmap' codec can't encode character '\ufffd' in position 5: chara
cter maps to <undefined>
>>> s.encode('iso8859_1')               三
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'latin-1' codec can't encode character '\ufffd' in position 5: ordin
al not in range(256)
>>> s.encode('cp437')                   四
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "D:\Users\Rosefinch\AppData\Local\Programs\Python\Python36-32\lib\encodings\cp437
.py", line 12, in encode
    return codecs.charmap_encode(input,errors,encoding_map)
UnicodeEncodeError: 'charmap' codec can't encode character '\ufffd' in position 5: chara
cter maps to <undefined>
>>> s.encode('cp437',errors="ignore")   五
b'Montral'
>>> s.encode('cp437',errors="replace")	六
b'Montr?al'
>>> s.encode('cp437',errors="xmlcharrefreplace")		七
b'Montr�al'

一。‘utf_?' 编码能处理任何字符串
二三四。 无法编码“�”。默认的错误处理方式‘strict’抛出UnicodeEncodeError
五。error=“ignore”处理方式消无声息地跳过无法编码的字符;这样做通常很是不妥。
六。编码是指定error=“replace”,把无法编码的字符替换成“?";数据损坏了,但是用户知道除了问题。
七。xmlcharrefreplace 把无法编码的字符替换成xml实体。


处理UnicodeDecodeError
>>> b = b'Montr\xe9al'		        一
>>> b.decode('cp1252')    		二
'Montréal'
>>> b.decode('iso8859_1')		三
'Montréal'
>>> b.decode('iso8859_7')		四
'Montrιal'
>>> b.decode('utf_8')			五
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xe9 in position 5: invalid continua
tion byte
>>> b.decode('utf_8',errors='replace')	六
'Montr�al'
一。这些字节序列就是用latin1编码的 'Montréal' ;‘\xe9' 字节对应“é”。
二三四。iso8859_7 用于编码希腊文,因此无法正确解释‘\xe9' 字节,而且没有抛出错误。
五。 ‘utf8’编码器检测到 b不是有效的“utf8" 字符串,抛出unicodeEecodeError
六。 使用’replace‘错误处理方式。\xe9 替换成”�“ (码位是U+FFFD);


继续看

import sys,io
print(sys.stdin.encoding) #标准输入的编码方式
print(sys.stdout.encoding) #标准输出的编码方式
s = 'Montr�al'	   #字符串
print(s.encode('utf8').decode('utf8'))    #以utf8编码,再以utf8编码解码
#在sublime编辑器运行
cp936
Traceback (most recent call last):
cp936
  File "D:\Users\Rosefinch\Desktop\test2.py", line 8, in <module>
[Decode error - output not utf-8]
UnicodeEncodeError: 'gbk' codec can't encode character '\ufffd' in position 5: illegal multibyte sequence
#在cmd命令符下运行
utf-8
utf-8
Montr�al

        由此可知,python3默认编码方式是utf-8,第三方编辑器默认编码是cp936(以windows 10操作系统默认编码是GBK为例);根据开始以什么方法编码最后也要以什么方式解码,不然你会遇到麻烦。

        回到上面sublime编辑器运行出现错误,原因是sublime编辑器没有指定输出编码方式,所以它会使用区域设置(windows操作系统)中的默认编码GBK方式输出。因为你以utf8方式解码,还得以utf8编码方式输出,而不是window操作系统的默认编码GBK,所以通过更改标准输出编码方式改变。

#在sublime代码添加
sys.stdout = io.TextIOWrapper(sys.stdout.buffer,encoding='utf8')  #更改标准输出编码
#在sublime运行
import sys,io

print(sys.stdin.encoding)
print(sys.stdout.encoding)
sys.stdout = io.TextIOWrapper(sys.stdout.buffer,encoding='utf8')  
s = 'Montr�al'	#字符串
print(s.encode('utf8').decode('utf8')) #以utf8编码,再以utf8解码

#运行
cp936
cp936
utf8
Montr锟絘l
  同理以GBK方式输出。
#在sublime运行
import sys,io

print(sys.stdin.encoding)
print(sys.stdout.encoding)
sys.stdout = io.TextIOWrapper(sys.stdout.buffer,encoding='utf8') #更改标准输出的编码方式
print(sys.stdout.encoding)
s = 'Montr�al'	#字符串
print(s.encode('utf8').decode('GBK')) #以utf8编码,再以GBK解码

#运行
cp936
cp936
utf8
Montr閿熺禈l

        上面以GBK解码,前面不是说也要以相应编码方式输出吗?为什么utf-8也可以,在最前已经说过“utf_?”编码能处理任何字符串。

       最后在《流畅的python》引入一个处理文本例子。

一个平台上的编码问题(如果在你的机器上运行,它可能会发生,也可能不会)

>>> fp = open('test.txt','w',encoding='utf-8').write('Montréal')
>>> fp
8
>>> open('test.txt').read()
'Montr茅al'
问题是:写入文件时指定了utf-8编码,但是读取文件时没有这么做,因此Python假定要使用系统默认的编码

cp936,于是文件的最后一个字节解码成了字符“茅" ,而不是 ”é“。

        我是在windows10中运行上面代码的。在新版GNU/Linux或Mac OS X中运行同样的语句不会出问题,因为这几个操作系统的默认编码是UTF-8,让人误以为一切正常。如果打开文件是为了写入,但是没有指定编码参数,会使用区域设置中的默认编码,而且使用那个编码也能正确读取文件。但是,如果脚本生成文件,而字节的内容取决于平台或同一平台中的区域设置,那么就可能导致兼容设置。

        因此,关于编码默认值哦最佳建议是:别依赖默认值。

仔细分析在windows中运行上面的代码,找出并修正问题。

>>> fp = open('test.txt','w',encoding='utf-8')
>>> fp        #默认情况下,open函数采用文本模式,返回一个TextIoWrapper对象。
<_io.TextIOWrapper name='test.txt' mode='w' encoding='utf-8'>
>>> fp.write('Montréal')
8        #在TextIoWrapper对象上调用write方法返回写入的Unicode字符数。
>>> fp.close()
>>> import os
>>> os.stat('test.txt').st_size
9        #os.stat报告文件中有9个字节,UTF-8编码的“ é “占两个字节,\xc3\xa9。
>>> fp2 = open('test.txt')
>>> fp2    #打开文本文件时没有显式指定编码,返回一个TextIoWrapper对象,编码是区域设置中的默认值。
<_io.TextIOWrapper name='test.txt' mode='r' encoding='cp936'>
>>> fp2.encoding     #TextIoWrapper对象有个encoding属性;查看它,发现这里的编码是cp936。
'cp936'
>>> fp2.read()
'Montr茅al'    #在cp936编码中,0xc3字节是“ é “, 0xa9字节是版权符号。
>>> fp3 = open('test.txt',encoding='utf_8')    #使用正确的编码打开那个文件
>>> fp3
<_io.TextIOWrapper name='test.txt' mode='r' encoding='utf_8'>
>>> fp3.read()
'Montréal'    #结果符号预期:得到的是8个Unicode字符 'Montréal' 。
>>> fp4 = open('test.txt','rb')    # 'rb' 标志指明在二进制模式中读取文件。
>>> fp4
<_io.BufferedReader name='test.txt'>    # 返回的是BufferedReader对象,而不是TextIoWrapper对象。
>>> fp4.read()
b'Montr\xc3\xa9al'    #读取返回的字节序列,结果与预期相符。
         除非想判断编码,否则不要在二进制模式中打开文本文件;即便如此,也应该使用Chardet,而不是重新发明轮子。常规代码只应该使用二进制模式打开二进制文件,如光栅图像。




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值