遇到亂碼問題,如何解決?

來源|Python七號(hào)
完美的世界是沒有亂碼的,但是我們的世界是不完美的,亂碼問題,你總有一天會(huì)遇到。
之前解決了一個(gè) Python 的 UnicodeEncodeError 問題,比較具有代表性,特此分享一下,希望可以幫到遇到此類問題的朋友。
通常情況下,Linux 默認(rèn)使用的字符編碼是 utf-8,windows 是 gbk,不跨操作系統(tǒng)的情況下,我們按此編碼存取數(shù)據(jù),文件和文件名均不會(huì)出現(xiàn)亂碼問題。
但現(xiàn)實(shí)就是要跨系統(tǒng)傳輸文件。如果文件名都是英文,也不會(huì)出現(xiàn)亂碼問題,因?yàn)橛⑽亩际?ascii 編碼,而所有的編碼都是包含 ascii 碼的,誰讓人家先發(fā)明了計(jì)算機(jī)呢。
然而,現(xiàn)實(shí)就是需要跨系統(tǒng)傳輸中文名稱的文件。
修改文件名的編碼
亂碼的原因就是編碼不一樣,比如說 Linux 一個(gè)中文名稱的文件,“中文.txt",實(shí)際上保存在磁盤上時(shí),對(duì)其做了 utf-8 的編碼,實(shí)際保存的就是字節(jié):"中文.txt".encode('utf-8'),這段字節(jié)傳輸?shù)?Windows 機(jī)器上時(shí)不會(huì)改變,當(dāng)你打開目錄查看時(shí),Windows 會(huì)按照 gbk 進(jìn)行解碼,就是 "中文.txt".encode('utf-8').decode('gbk'),編碼和解碼用到的字符集不一樣,自然會(huì)亂碼。
解決辦法就是保存文件時(shí),修改文件名的編碼,怎么修改?
先來看下 Python 內(nèi)建的 open 函數(shù)簽名:

這里的 encoding 參數(shù)是指定文件內(nèi)容的字符編碼,而不是文件名的編碼,因此我們需要關(guān)注 file 這個(gè)參數(shù),file 是一個(gè)像路徑一樣的對(duì)象,點(diǎn)擊 path-like object 可以看到說明:

也就是說,file 可以是一個(gè)字符串,也可以是字節(jié)串,那就好辦了,假如要在 Linux 環(huán)境保存一個(gè)文件名是 gbk 編碼的文件,可以這樣做:
file_name = "中文.txt"
with open(file_name.encode('gbk'), "w",encoding = 'your file content encoding') as write:
# do something
這個(gè)文件在 Linux 下的文件名看起來是亂碼,但傳輸?shù)?Windows 就是正常顯示的。
ftplib 傳輸?shù)木幋a問題
我在數(shù)倉做數(shù)據(jù)交換的時(shí)候,通常要 Linux 和 Windows 互相傳文件,為此專門寫過一個(gè)通用的傳輸文件庫 transferfile[1],就遇到了兩個(gè)編碼問題:
1、Linux 向 Windows 傳輸?shù)奈募形膩y碼。
解決方法:ftplib 考慮到了這一點(diǎn),在 ftplib.FTP 初始化后可以傳入 encoding 參數(shù),來指定目標(biāo)系統(tǒng)以何種編碼保存文件名稱。如下圖所示:

2、Windows 下有亂碼路徑時(shí)無法在 Windows 里面遞歸的創(chuàng)建目標(biāo)路徑。
傳輸文件時(shí)可以指定目標(biāo)路徑,路徑不存在時(shí)需要遞歸創(chuàng)建,以便存放上傳的文件,ftplib 本身只能創(chuàng)建一個(gè)目錄,需要自行寫遞歸創(chuàng)建,那么創(chuàng)建之前就要先判斷目錄是否存在,這就需要用到 ftp.retrlines('LIST'),能列出來的就是存在的。
如果目錄內(nèi)沒有亂碼文件,也不會(huì)有問題,有就會(huì)報(bào) UnicodeEncodeError,如下圖:

怎么解決呢?
那就是不能讓亂碼目錄影響了我們的主程序,在讀取列表時(shí)遇到亂碼忽略即可,按照 traceback 修改標(biāo)準(zhǔn)庫 ftplib 文件 471 行,傳入?yún)?shù) errors='ignore' 如下圖所示

標(biāo)準(zhǔn)庫為什么不直接加上呢?我猜測(cè)就是為了讓你知道,這里存在亂碼,要忽略的話,自己搞定,我不為你背鍋,??。
最后
本文介紹了亂碼問題的原因,如何修改文件名稱的編碼,用 ftplib 遇到的編碼問題如何解決,雖然場(chǎng)景具體,但解決的亂碼問題的思路都是一樣的,那就是讓編碼解碼使用的字符編碼保持一致,如果亂碼不影響可以忽略掉不能解碼的數(shù)據(jù)。
參考資料
transferfile: https://pypi.org/project/transferfile/
