python2编码报错整理
0x01 SyntaxError: Non-ASCII character
先从最简单的python编码报错说起,本文所有演示的python文件存储格式默认为utf-8。
创建exploit.py文件,内容如下:
print "测试"
此时,执行该脚本,会报如下错误:
> python exploit.py
File "exploit.py", line 1
SyntaxError: Non-ASCII character '\xe6' in file exploit.py on line 1, but no encoding declared; see http://python.org/dev/peps/pep-0263/ for details
这是由于python2 默认python脚本文件编码格式为ascii,而我们保存的文件格式是utf-8,python解释器在处理脚本时识别不了0xe6
这个字符,所以会报错,对此我们需要在脚本开头声明文件编码格式,如下所示:
#coding=utf-8
print "测试"
这样python就会知道我们当前这个脚本文件的编码格式是utf-8。
0x02 IOError: [Errno 28] & 乱码
在windows cmd下执行上述脚本,输出如下:
> python exploit.py
娴嬭瘯
这是由于当前执行python脚本的cmd窗口的编码格式是gbk,“测试”两个字的utf-8编码对应的是“娴嬭瘯”三个字的gbk编码,为了显示正常,我们需要将字符串解码成unicode,然后在编码为gbk(也可直接输出,print输出时会根据环境进行自动编码)
#coding=utf-8
print "测试".decode("utf-8")
## or
print "测试".decode("utf-8").encode("gbk")
这样就会输出正常了,有时输出可能不是乱码,而是报IOError错误,这也是由于当前输出内容对于cmd窗口来说无法正常识别,
> python 1.py
?Traceback (most recent call last):
File "1.py", line 3, in <module>
print "\xf3\xff"
IOError: [Errno 28] No space left on device
除了调用decode函数,将字符串按其编码格式转换为unicode外,我们还可以在cmd窗口执行chcp命令,修改当前cmd窗口的编码格式,保证与脚本编码一致
chcp 65001 #转换为utf-8代码页
chcp 936 #转换为默认的gbk
chcp 1252 #转换为拉丁字符集
0x03 UnicodeDecodeError: 'xxx' codec can't
这种报错比较常见,网上给出的解决方案一般是在文件头添加如下代码
#coding=utf-8
import sys
if sys.getdefaultencoding() != 'utf-8':
reload(sys)
sys.setdefaultencoding('utf-8')
这种方式有时可以解决问题,有时候不能解决。这是由于这并不是一个很好的解决方案,我们需要深入了解一下,这个报错的原因。
首先说一下python2的两个函数 encode 和decode
## 将字符串按utf-8解码,返回的是对应字符串的unicode对象
"测试".decode("utf-8")
## 返回unicode对象按utf-8进行编码的字符串
u"测试".encode("utf-8")
UnicodeDecodeError一种情况是发生在调用decode函数时,当指定的解码方式无法正确解码指定的字符串时,就会报UnicodeDecodeError,如以下代码:
print "\xff\xff".decode("utf-8")
utf-8编码表中无以0xff
开头的字符,就回报错:
UnicodeDecodeError: 'utf8' codec can't decode byte 0xff in position 0: invalid start byte
还要一种常见的报错,
UnicodeDecodeError: 'ascii' codec can't decode
提示无法按ascii进行解码,以如下代码为例:
#coding=utf-8
print "测试".decode()
执行时就会报上面的错误,这是由于python的默认编码格式就是ascii,当我们进行docode时不指定解码方式,就会按默认的ascii进行解码,而ascii无法识别0x80~0xff
的字符,因此就回报这个错误,这是如果我们在代码开头加上如下代码:
#coding=utf-8
import sys
if sys.getdefaultencoding() != 'utf-8':
reload(sys)
sys.setdefaultencoding('utf-8')
就会使字符串按默认的utf-8进行解码,从而就不会报错了。但是这种解决方式其实不如在解码时直接指定编码格式,尽可能少的对其他代码造成影响。
#coding=utf-8
print "测试".decode("utf-8")
UnicodeDecodeError更常见的报错时发生在我们没有调用decode方法,而是调用encode方法时,经过实验发现,encode方法主要是用于将unicode对象转换为字符串,但是当对一个字符串使用encode方法时,如:
"测试".encode("utf-8")
会发生什么呢?经测试会报如下错误
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe6 in position 0: ordinal not in range(128)
这是由于对字符串调用encode方法时,会首先尝试按默认编码格式(也就是ascii)将字符串decode为unicode,然后再将unicode按指定的方式encode为字符串,所以就会报decode错了。
总结:在遇到UnicodeDecodeError报错时,不要着急通过 reload(sys)修改系统编码,先检查是不是encode和decode搞错了,对字符串调用了encode,或者时调用decode时未指定或指定错了编码格式。
对于git别人的代码报这个错误,先检查一下是不是当前路径包含了中文。
0x04 打印二进制字符流
输出不全
有时候再用python处理非文本文件或网络请求时,直接使用print会发现输出的内容只有一部分,以如下代码为例:
print "\x61\x62\x63\x64\x0d\x65\x66"
只会输出efcd
,这是由于字符中出现了\x0d
,也就是回车,会将原有输出内容覆盖掉,在输出钱可以考虑将\x0d
和替换掉,或者对输出的字符串进行base64编码,然后再用其他工具解码
报错
由于二进制字符流中可能出现任意\x00~\xff
的字符,因此无论用何种方式解码,输出使都有可能报错,如下所示:
#coding=utf-8
try:
print "\xf3\xff\xff\xfd\x61\x62\x63"
except Exception as e:
print repr(e)
try:
print "\xf3\xff\xfd\x61\x62\x63".decode("ascii")
except Exception as e:
print repr(e)
try:
print "\xf3\xff\xfd\x61\x62\x63".decode("utf-8")
except Exception as e:
print repr(e)
try:
print "\xf3\xff\xfd\x61\x62\x63".decode("gbk")
except Exception as e:
print repr(e)
try:
print "\xf3\xff\xfd\x61\x62\x63".decode("iso8859-1")
except Exception as e:
print repr(e)
try:
print "\xf3\xff\xfd\x61\x62\x63".decode("ascii","ignore")
except Exception as e:
print repr(e)
输出结果:
?齛bcIOError(2, 'No such file or directory')
UnicodeDecodeError('ascii', '\xf3\xff\xfdabc', 0, 1, 'ordinal not in range(128)')
UnicodeDecodeError('utf8', '\xf3\xff\xfdabc', 0, 1, 'invalid continuation byte')
UnicodeDecodeError('gbk', '\xf3\xff\xfdabc', 0, 2, 'illegal multibyte sequence')
UnicodeEncodeError('gbk', u'\xf3\xff\xfdabc', 1, 2, 'illegal multibyte sequence')
abc
先说结论:print "\xf3\xff\xfd\x61\x62\x63".decode("ascii","ignore")
这种输出方式最佳,自动忽略无法进行ascii解码的字符(记得对\x0d
进行处理),
iso8859-1编码虽然可以识别\x00~\xff
的所有字符,但是在输出时,无法将所有字符都转为gbk(cmd窗口的默认编码格式为gbk),所以会报UnicodeEncodeError错误。
当然,先将cmd窗口编码转为拉丁字符集 chcp 1252
然后再按iso8859-1解码输出,效果更好:
print "\xf3\xff\xfd\x0d\x61\x62\x63".decode("iso8859-1").replace("\x0d","\x0d\x0a")
> python 1.py
óÿý
abc