完美的世界是没有乱码的,但是我们的世界是不完美的,乱码问题,你总有一天会遇到。
之前解决了一个 Python 的 UnicodeEncodeError 问题,比较具有代表性,特此分享一下,希望可以帮到遇到此类问题的朋友。
通常情况下,Linux 默认使用的字符编码是 utf-8,windows 是 gbk,不跨操作系统的情况下,我们按此编码存取数据,文件和文件名均不会出现乱码问题。
但现实就是要跨系统传输文件。如果文件名都是英文,也不会出现乱码问题,因为英文都是 ascii 编码,而所有的编码都是包含 ascii 码的,谁让人家先发明了计算机呢。
然而,现实就是需要跨系统传输中文名称的文件。
修改文件名的编码
乱码的原因就是编码不一样,比如说 Linux 一个中文名称的文件,“中文.txt",实际上保存在磁盘上时,对其做了 utf-8 的编码,实际保存的就是字节:"中文.txt".encode('utf-8'),这段字节传输到 Windows 机器上时不会改变,当你打开目录查看时,Windows 会按照 gbk 进行解码,就是 "中文.txt".encode('utf-8').decode('gbk'),编码和解码用到的字符集不一样,自然会乱码。
解决办法就是保存文件时,修改文件名的编码,怎么修改?
先来看下 Python 内建的 open 函数签名:
这里的 encoding 参数是指定文件内容的字符编码,而不是文件名的编码,因此我们需要关注 file 这个参数,file 是一个像路径一样的对象,点击 path-like object
可以看到说明:
也就是说,file 可以是一个字符串,也可以是字节串,那就好办了,假如要在 Linux 环境保存一个文件名是 gbk 编码的文件,可以这样做:
file_name = "中文.txt"
with open(file_name.encode('gbk'), "w",encoding = 'your file content encoding') as write:
# do something
这个文件在 Linux 下的文件名看起来是乱码,但传输到 Windows 就是正常显示的。
ftplib 传输的编码问题
我在数仓做数据交换的时候,通常要 Linux 和 Windows 互相传文件,为此专门写过一个通用的传输文件库 transferfile[1],就遇到了两个编码问题:
1、Linux 向 Windows 传输的文件中文乱码。
解决方法:ftplib 考虑到了这一点,在 ftplib.FTP 初始化后可以传入 encoding 参数,来指定目标系统以何种编码保存文件名称。如下图所示:
2、Windows 下有乱码路径时无法在 Windows 里面递归的创建目标路径。
传输文件时可以指定目标路径,路径不存在时需要递归创建,以便存放上传的文件,ftplib 本身只能创建一个目录,需要自行写递归创建,那么创建之前就要先判断目录是否存在,这就需要用到 ftp.retrlines('LIST'),能列出来的就是存在的。
如果目录内没有乱码文件,也不会有问题,有就会报 UnicodeEncodeError,如下图:
怎么解决呢?
那就是不能让乱码目录影响了我们的主程序,在读取列表时遇到乱码忽略即可,按照 traceback 修改标准库 ftplib 文件 471 行,传入参数 errors='ignore' 如下图所示
标准库为什么不直接加上呢?我猜测就是为了让你知道,这里存在乱码,要忽略的话,自己搞定,我不为你背锅,????。
最后
本文介绍了乱码问题的原因,如何修改文件名称的编码,用 ftplib 遇到的编码问题如何解决,虽然场景具体,但解决的乱码问题的思路都是一样的,那就是让编码解码使用的字符编码保持一致,如果乱码不影响可以忽略掉不能解码的数据。
「二、字符串编码」
感谢阅读。