公司微網(wǎng)站建設(shè)方案網(wǎng)絡(luò)營銷的基本流程
文章目錄
- 參考
- 描述
- 鋪墊
- 亂碼現(xiàn)象
- 編碼與解碼
- 編碼
- 解碼
- 字符集
- Unicode 字符集
- UTF-8
- CP437
- Zip 文件與 CP437 編碼
- GB2312
- GB2012
- GBK
- 單字節(jié)編碼與多字節(jié)編碼
- 溯源
- 通用標(biāo)志位與語言編碼標(biāo)志
- ZipFile 所支持的兩種編碼方式
- GBK 編碼與 Zip 應(yīng)用
- 亂碼現(xiàn)象產(chǎn)生的原因
- 解決
參考
項(xiàng)目 | 描述 |
---|---|
維基百科 | ZIP 格式 |
Python 官方文檔 | zipfile - 使用ZIP存檔 |
搜索引擎 | Google、Bing |
Zip 文件格式規(guī)范 | APPNOTE.TXT |
維基百科 | 首頁 |
百度百科 | 首頁 |
描述
項(xiàng)目 | 描述 |
---|---|
Python | 3.10.6 |
操作系統(tǒng) | Windows 10 專業(yè)版(x86-64) |
壓縮軟件 | 360壓縮(4.0.0.1460) |
鋪墊
本部分內(nèi)容為鋪墊內(nèi)容,除 “亂碼現(xiàn)象” 為必看內(nèi)容外,其余均為選看內(nèi)容。如本部分內(nèi)容已觀看完畢,請移步至 “溯源” 部分繼續(xù)觀看。
亂碼現(xiàn)象
在使用國內(nèi)主流的 Zip 壓縮軟件將文件進(jìn)行壓縮后,若使用 Python 模塊 ZipFile 對 Zip 文件中被存儲文件的名稱進(jìn)行提取,往往會產(chǎn)生亂碼現(xiàn)象。對此,請參考如下示例:
import zipfile# 月亮與六便士.zip 文件是使用 360壓縮將
# 月亮與六便士.txt 文件 進(jìn)行壓縮后得到的產(chǎn)物。
ZIPPATH = r'C:\Users\RedHeart\PycharmProjects\pythonProject\月亮與六便士.zip'# 以只讀方式打開 Zip 文件
with zipfile.ZipFile(ZIPPATH) as fz:# 迭代名稱列表,輸出 Zip 文件中所有文件的名稱for name in fz.namelist():print(name)
執(zhí)行效果
╘┬┴┴╙δ┴??π╩┐.txt
編碼與解碼
編碼
編碼通常是為了將文本進(jìn)行存儲、傳輸或處理,因?yàn)?計(jì)算機(jī)只能處理數(shù)字。常見的編碼方式包括 ASCII、Unicode、UTF-8 等。
解碼
解碼是指將已經(jīng)被編碼的數(shù)字或數(shù)字序列轉(zhuǎn)換回原始的字符或文本的過程。解碼的過程是編碼的逆過程,通常需要使用與編碼方式相同的規(guī)則和算法來進(jìn)行解碼。
字符集
在計(jì)算機(jī)中,字符集(Character Set)是由一系列字符組成的集合,每個字符都對應(yīng)一個唯一的編碼。常見的字符集包括 ASCII、Unicode 等。
Unicode 字符集
Unicode 字符集中的一種,它包含了全球所有語言中的字符,并且可以進(jìn)行擴(kuò)展,支持更多的字符。 Unicode 字符集目前已經(jīng)超過了 100,000 個字符,其中包括了各種字母、數(shù)字、標(biāo)點(diǎn)符號、符號、表情符號等。
Unicode 的編碼方式有多種,其中比較常見的是 UTF-8、UTF-16 和 UTF-32。不同的編碼方式使用不同的位數(shù)來表示一個字符,UTF-8 使用 1 到 4 個字節(jié)來表示一個字符,UTF-16 使用 2 或 4 個字節(jié)來表示一個字符,UTF-32 使用 4 個字節(jié)來表示一個字符。
Unicode 字符集的出現(xiàn)解決了多語言字符集的混亂問題,使得各個語言之間的交流和互動更加方便和自然。 在計(jì)算機(jī)領(lǐng)域,Unicode 的廣泛使用也使得字符編碼的處理更加便捷和標(biāo)準(zhǔn)化。
UTF-8
UTF-8(Unicode Transformation Format-8) 是一種 可變長度 的字符編碼方式,它是 Unicode 字符集 中的一種。
UTF-8 是一種可變長度的編碼方式,可以用來表示 Unicode 中的任何字符。UTF-8 編碼方式使用 1 到 4 個字節(jié)來表示一個字符,較少使用的字符使用數(shù)量較多的字節(jié),而常用的字符則使用數(shù)量較少的字節(jié),以便節(jié)約存儲空間。
除了 UTF-8 之外,Unicode 還存在其他的編碼方式,如 UTF-16 和 UTF-32,但是在實(shí)際應(yīng)用中,UTF-8 是最為廣泛使用的編碼方式之一。
CP437
CP437 是字符集中的一種,也稱為 IBM PC 字符集 或 DOS 字符集。它最初是為 IBM PC 設(shè)計(jì)的,用于在早期的個人計(jì)算機(jī)上顯示字符和符號。CP437 包含了 256 個字符,包括了字母、數(shù)字、標(biāo)點(diǎn)符號、符號以及各種特殊符號等。
CP437 的編碼方式是一個字節(jié)表示一個字符,它使用了 ASCII 編碼中未被使用的 128 個字符位置來表示新的字符和符號。因此,CP437 可以被認(rèn)為是 ASCII 字符集的擴(kuò)展版本。
CP437 主要用于早期的個人計(jì)算機(jī)系統(tǒng),例如 IBM PC 和 DOS 系統(tǒng)。雖然它已經(jīng)被現(xiàn)代的 Unicode 字符集所取代,但是在一些舊的系統(tǒng)和應(yīng)用中,仍然會使用 CP437 編碼方式。
Zip 文件與 CP437 編碼
ZIP 文件在歷史上并沒有指定元數(shù)據(jù)信息的編碼方式,但強(qiáng)烈推薦使用 CP437 編碼(原始的 IBM PC 編碼)以實(shí)現(xiàn)互操作性。這是因?yàn)?CP437 編碼是一個非?;A(chǔ)的編碼方式,幾乎所有計(jì)算機(jī)系統(tǒng)都能夠識別它。
當(dāng)然,現(xiàn)代的 ZIP 實(shí)現(xiàn)已經(jīng)支持使用 UTF-8 編碼來存儲文件名和注釋信息。但是,由于歷史原因和兼容性問題,一些舊的 ZIP 工具可能仍然只支持使用 CP437 編碼。因此,為了確保 ZIP 文件的兼容性,強(qiáng)烈建議在創(chuàng)建 ZIP 文件時使用 CP437 進(jìn)行編碼。如果需要使用其他編碼方式進(jìn)行解碼,則應(yīng)將其轉(zhuǎn)換為 CP437 編碼再對其進(jìn)行解碼操作。
GB2312
GB2312 是中國國家標(biāo)準(zhǔn)中的一種字符集編碼方式,它最初于 1980 年發(fā)布,是為了在計(jì)算機(jī)系統(tǒng)中表示漢字而開發(fā)的。GB2312 字符集中包含了大約 7,000 個常用漢字和約 700 個非漢字字符,例如數(shù)字、字母和符號等。
GB2312 使用雙字節(jié)編碼方式,每個漢字使用兩個字節(jié)來表示,而每個非漢字字符(如數(shù)字、字母和符號)使用一個字節(jié)來表示。使用 GB2312 編碼方式的文件可以在大多數(shù)中文操作系統(tǒng)和應(yīng)用程序中正確顯示漢字和其他字符。
GB2012
GB2012(GB/T 2312-2017)是 GB2312 字符集的升級版,與 GBK 有一些類似之處,它也使用雙字節(jié)編碼,每個漢字使用 2 個字節(jié)來表示,而每個非漢字字符使用一個字節(jié)表示。GB2012 擴(kuò)展了 GB2312 中的字符集,增加了一些生僻漢字和其他字符。與 GBK 不同的是,GB2012 中新增的字符主要來自于香港和臺灣地區(qū)的繁體中文字符集,因此在某些情況下,GB2012 可能比 GBK 更適合表示繁體中文。
GBK
GBK 是在 GB2312 字符集的基礎(chǔ)上進(jìn)行擴(kuò)展的編碼方式,它包含了 GB2312 中的所有漢字,并擴(kuò)展了更多的漢字和其他字符,使得它能夠表示包括繁體中文在內(nèi)的大部分東亞語言中的字符。GBK 使用雙字節(jié)編碼,每個漢字使用 2 個字節(jié)來表示,而每個非漢字字符(如英文、數(shù)字和符號)使用一個字節(jié)表示。GBK 是在中國大陸廣泛使用的字符集編碼方式,也是 Windows 操作系統(tǒng)中默認(rèn)的中文字符集編碼方式之一。
單字節(jié)編碼與多字節(jié)編碼
單字節(jié)編碼是一種字符編碼方式,其中 每個字符使用一個固定的字節(jié)表示。例如,在 ASCII 編碼中,每個字符都用一個字節(jié)表示,字節(jié)的值對應(yīng)于該字符在 ASCII 表中的位置。
多字節(jié)編碼是另一種字符編碼方式,其中每個字符使用多個字節(jié)來表示。 例如,在 UTF-8 編碼中,一個字符可以由 1 到 4 個字節(jié)組成,具體的字節(jié)數(shù)量取決于字符所在的 Unicode 編碼范圍。
單字節(jié)編碼在存儲和傳輸文本數(shù)據(jù)時通常比多字節(jié)編碼更簡單、更高效,因?yàn)槊總€字符只需要一個固定大小的字節(jié)。然而,由于單字節(jié)編碼只能表示有限的字符集,不能滿足全球化和多語言支持的需求,因此多字節(jié)編碼在現(xiàn)代計(jì)算機(jī)系統(tǒng)中得到了廣泛的應(yīng)用。
溯源
通用標(biāo)志位與語言編碼標(biāo)志
通用位標(biāo)志 (General Purpose Bit Flag) 是一組用于 ZIP 文件頭部的二進(jìn)制位標(biāo)記,大小為兩個字節(jié)(即 16 bits),用于指示 ZIP 文件中的各種信息,例如文件是否使用了壓縮算法,是否加密等。EFS (Language encoding flag) 是其中的一位標(biāo)記,即第 11 位,用于指示 ZIP 文件中的文件名和注釋信息采用的字符編碼方式是否為 UTF-8。
當(dāng) ZIP 文件中的文件名和注釋信息 都 采用 UTF-8 編碼時,可以設(shè)置 EFS 標(biāo)志來表示這一信息(據(jù)說,可以提高與早期 Zip 文件的兼容性)。這樣,在讀取 ZIP 文件時,程序就能夠正確地解析文件名和注釋信息的編碼方式,從而正常地顯示這些信息 (在 ZipFile 中,獲取文件注釋信息d的結(jié)果為一個字節(jié)串,你需要自己對它們進(jìn)行解碼來獲取注釋信息)。
ZipFile 所支持的兩種編碼方式
ZipFile 模塊僅使用 CP437 及 UTF-8 編碼對文件路徑及注釋進(jìn)行解碼。ZipFile 模塊首先將判斷 Zip 內(nèi)存儲的文件路徑是否是使用 UTF-8 進(jìn)行編碼的。若是,則使用 UTF-8 對其進(jìn)行解碼;若不是,則使用 CP437 對其進(jìn)行解碼。關(guān)于這點(diǎn),我們可以從 ZipFile 模塊的源碼中窺見一斑。
if flags & 0x800:# UTF-8 file names extensionfilename = filename.decode('utf-8')
else:# Historical ZIP filename encodingfilename = filename.decode('cp437')
其中:
flags 代表Zip文件中某個文件的通用標(biāo)志位。在這個 if-else 語句 中,程序會首先檢查該文件的通用標(biāo)志位的 第 11 位 是否為 1(通過將 flags 與 0x800 進(jìn)行按位與操作來達(dá)成此目標(biāo)),如果為 1 則表示該文件的文件名使用了 UTF-8 編碼,需要使用 UTF-8 進(jìn)行解碼;否則表示該文件的文件名使用了歷史的 ZIP 文件名編碼(即CP437),需要使用 CP437 進(jìn)行解碼。
GBK 編碼與 Zip 應(yīng)用
ZIP 文件格式本身并沒有規(guī)定文件路徑與注釋信息必須使用哪種編碼方式進(jìn)行存儲。如果壓縮軟件開發(fā)者不進(jìn)行特別的處理,可能會采用默認(rèn)的編碼方式,例如在中國地區(qū),許多壓縮軟件默認(rèn)使用 GBK 編碼方式來處理文件名。
在國內(nèi),一些壓縮軟件將文件路徑及注釋使用GBK編碼進(jìn)行處理,以下是一些常見的壓縮軟件:
-
360壓縮
-
傲游壓縮
-
金山壓縮
亂碼現(xiàn)象產(chǎn)生的原因
在使用國內(nèi)主流的壓縮軟件將文件壓縮為 Zip 文件時,會使用 GBK 對文件路徑及 Zip 文件的注釋信息進(jìn)行編碼。而 ZipFile 模塊在檢測出該 Zip 文件未設(shè)置 語言編碼標(biāo)志 后,使用 CP437 對文件路徑進(jìn)行解碼,于是就得到了我們所看到的亂碼現(xiàn)象。
對 “亂碼現(xiàn)象” 中存在的編碼與解碼操作進(jìn)行模擬
filename = '月亮與六便士.txt'# 將 filename 進(jìn)行 GBK 編碼
gbk_filename = filename.encode('GBK')
print(gbk_filename)# 使用 CP437 對使用 GBK 編碼后的結(jié)果進(jìn)行解碼操作
result = gbk_filename.decode('CP437')
print(result)
執(zhí)行效果
使用 CP437 解碼使用 GBK 編碼的字符串得到的結(jié)果與本文開頭所介紹的亂碼現(xiàn)象中出現(xiàn)的亂碼完全一致。
b'\xd4\xc2\xc1\xc1\xd3\xeb\xc1\xf9\xb1\xe3\xca\xbf.txt'
╘┬┴┴╙δ┴??π╩┐.txt
解決
我們得到的亂碼文字是由于將 GBK 編碼的字節(jié)串使用 CP437 對其進(jìn)行解碼后得到的,那么我們僅需要將亂碼文字再次編碼為 CP437 并對其進(jìn)行 GBK 解碼操作就能夠得到正確的文件名稱了。對此,我們對代碼進(jìn)行如下改造:
import zipfile# 月亮與六便士.zip 文件是使用 360壓縮將
# 月亮與六便士.txt 文件 進(jìn)行壓縮后得到的產(chǎn)物。
ZIPPATH = r'C:\Users\RedHeart\PycharmProjects\pythonProject\月亮與六便士.zip'# 以只讀方式打開 Zip 文件
with zipfile.ZipFile(ZIPPATH) as fz:# 迭代名稱列表,輸出 Zip 文件中所有文件的名稱for name in fz.namelist():# 對文件名稱進(jìn)行編碼和解碼操作result = name.encode('CP437').decode('GBK')print(result)
執(zhí)行效果
月亮與六便士.txt