编码这个词搞计算机的人肯定都不陌生,这里讨论的编码特指字符串的编码解码,撇开特定计算机语言,通俗的来说字符串的编码和解码就是字符串和二进制字节串(以下用bytes代替,bytes也是python3中的一种类型)的互相转换,而我们常说的gb2312、utf-8等编码类型则是规定了不同的转换规则。
1.编码:指定编码类型将一个字符串转换成bytes。比如我们将字符串写入文件就需要写入bytes,因此需要编码。
2.解码:指定编码类型将一个bytes转成字符串。
总的来说,我们所说的编码问题本质上都是各种原因造成的编码和解码所用的编码类型不一致所引起的,要保证编码解码不出问题,核心就只有一点:
在对bytes解码的过程中使用和当初它编码时使用的相同编码类型,也就是控制编码解码所用的编码类型一致。
只要是涉及到字符串(文本)的场景就避免不了编码解码,具体到python3这个场景里,个人认为可以分为三大类:
在具体分析各个场景之前我们先了解下一些python3编码解码相关的默认值,在很多场景如果我们不指定都会使用默认值,因此有必要了解这些默认值在我们执行脚本的环境里是什么:
#python3解释器系统的默认编码 print(sys.getdefaultencoding()) #获取操作系统的locale配置,linux下和shell的locale命令一致。 #python也提供了一些api修改locale,这里不展开,有需要可以自行查阅文档。 print(locale.getdefaultlocale())
下面我们就来看看这几类场景如何保证编码解码所用的编码类型一致。
python3直接提供了编码解码函数用于字符串到字节码之间的互相转换,如下:
1.编码:str.encode(encoding=‘UTF-8’,errors=‘strict’),返回bytes 对象
2.解码:bytes.decode(encoding=“utf-8”, errors=“strict”),返回字符串
函数很好理解,这里就不多展开了,一句话总结就是指定编码类型对字符串进行编码得到bytes、对bytes进行解码得到字符串,二者均可明确指定编码类型,不太容易出错。
这个场景的编码解码涉及到以下两点:
对于编辑器保存时候的编码不用多说,都可以设置。对于后者,默认是使用sys.getdefaultencoding()所指明的编码类型,这个编码类型在linux平台下就是utf-8,如果我们想手动指定,可以通过在脚本开头添加#coding=xxx来指定,有一点需要特别注意,这个仅仅影响解释器读取脚本过程中的解码,很多人会误以为还会影响其他地方的编码设置。
python经常用于处理文本,最常见的一种场景就是使用open打开文件从文件输入或者输出到文件,无论是读还是写,我们在打开文件的时候都可以用encoding指定编码类型,比如读取
with open('filename', 'r', encoding='utf-8') as file: for line in file: do sth
写入
with open('filename', 'w', encoding='utf-8') as file: f.write(xxx) print("字符串", file=f)
通过指定相同的encoding参数就可以保证一致,如果是读取外部生成的文件,也只需知道其编码便方便地指定。需要注意的是如果我们不指定,会使用和locale.getdefaultlocale()一致的编码。
操作标准的文本输入输出流本质上和操作文件是一样的也是一样的,比如我们常见的读取stdin
for line in sys.stdin: do sth
但stdin却不像文件一样可以直接显示指定编码类型,如果碰到了相关的编码问题相对更容易让人疑惑,对于stdin、stdout、stderr,python3有一套默认的编码选择规则,比如python 3.7的官方文档解释如下:
可以看到,对于linux,默认使用的是locale encoding,如此一来,对于locale encoding不是utf-8的linux环境就很有可能出问题,毕竟现在我们保存文件最常用的编码就是utf-8,比如hadoop streaming场景,如果locale encoding不是utf-8,但是用于cat的文本文件是utf-8的,就会导致使用非utf-8编码类型去解码utf-8编码出来的字节码从而产生问题。
对于此类问题主要有以下几个方法可以解决:
export PYTHONIOENCODING=utf-8
import io import sys input_stream = io.TextIOWrapper(sys.stdin.buffer, encoding='utf-8')
sys.stdin.reconfigure(encoding='utf-8')
在碰到python3的编码问题时,首先要做地就是分析具体是哪块的编码解码不一致,进而针对性地修改解决,而不是用笼统的“python3编码问题”一通搜索修改,还是要理解真正的具体原因,这样才能够快速准确地解决问题。
参考:
1.python-3-how-to-specify-stdin-encoding
2.吐血总结,彻底明白 python3 编码原理