這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
Go的標準庫已經內建了zip的庫.
不過zip包在處理內部檔案名稱時, 預設是utf8編碼的.
對於Windows中文使用者, 產生和讀取zip內部檔案名稱預設是GBK編碼的.
因此, 在處理涉及GBK的檔案名稱時需要做一個轉換.
Go語言官方的 go.text 子標準庫已經支援各種編碼, 下面是utf8轉GBK的函數:
import "golang.org/x/text/encoding/simplifiedchinese"func utf8ToGBK(text string) (string, error) { dst := make([]byte, len(text)*2) tr := simplifiedchinese.GB18030.NewEncoder() nDst, _, err := tr.Transform(dst, []byte(text), true) if err != nil { return text, err } return string(dst[:nDst]), nil}
在產生zip檔案時, 用 utf8ToGBK 處理檔案名稱:
func main() { file, err := os.Create("中文-測試.zip") if err != nil { log.Fatal(err) } defer file.Close() wzip := zip.NewWriter(file) defer func() { if err := wzip.Close(); err != nil { log.Fatal(err) } }() // 壓縮檔 var files = []struct{ Name, Body string }{ {"11/1/readme.txt", "UTF8 字串."}, {"11/1/readme2.txt", "This archive contains some text files."}, {"漢字/2/gopher.txt", "Gopher names:\nGeorge\nGeoffrey\nGonzo"}, {"11/中文.txt", "中文Get animal handling licence.\nWrite more examples."}, {"空目錄/", ""}, } for _, file := range files { name, _ := utf8ToGBK(file.Name) // 檔案名稱轉換為 GBK編碼 f, err := wzip.Create(name) if err != nil { log.Fatal(err) } _, err = f.Write([]byte(file.Body)) if err != nil { log.Fatal(err) } }}
這樣就可以產生Windows下帶簡體中文的檔案名稱壓縮檔了.
2014年補充:
其實在新的 zip規範 中,
已經對UTF8編碼的檔案名稱提供了支援.
File: APPNOTE.TXT - .ZIP File Format SpecificationVersion: 6.3.34.4.4 general purpose bit flag: (2 bytes)Bit 11: Language encoding flag (EFS). If this bit is set, the filename and comment fields for this file MUST be encoded using UTF-8. (see APPENDIX D)
具體來說, 在每個檔案的頭資訊的Flags欄位的11bit位.
如果該bit位為0則表用本地編碼(本地編碼是GBK嗎?), 如果是1則表示用UTF8編碼.
頭資訊對應zip庫的 archive/zip.FileHeader 結構的 Flags 成員:
type FileHeader struct { // Name is the name of the file. // It must be a relative path: it must not start with a drive // letter (e.g. C:) or leading slash, and only forward slashes // are allowed. Name string CreatorVersion uint16 ReaderVersion uint16 Flags uint16 Method uint16 ModifiedTime uint16 // MS-DOS time ModifiedDate uint16 // MS-DOS date CRC32 uint32 CompressedSize uint32 // deprecated; use CompressedSize64 UncompressedSize uint32 // deprecated; use UncompressedSize64 CompressedSize64 uint64 UncompressedSize64 uint64 Extra []byte ExternalAttrs uint32 // Meaning depends on CreatorVersion Comment string}
如果想產生UTF8編碼的檔案名稱, 可以手工指定該欄位:
func main() {
file, err := os.Create("中文-測試.zip")if err != nil { log.Fatal(err)}defer file.Close()wzip := zip.NewWriter(file)defer func() { if err := wzip.Close(); err != nil { log.Fatal(err) }}()// 壓縮檔var files = []struct{ Name, Body string }{ {"11/1/readme.txt", "UTF8 字串."}, {"11/1/readme2.txt", "This archive contains some text files."}, {"漢字/2/gopher.txt", "Gopher names:\nGeorge\nGeoffrey\nGonzo"}, {"11/中文.txt", "中文Get animal handling licence.\nWrite more examples."}, {"空目錄/", ""},}for _, file := range files { header := &zip.FileHeader{ Name: file.Name, Flags: 1 << 11, // 使用utf8編碼 Method: zip.Deflate, } f, err := wzip.CreateHeader(header) if err != nil { log.Fatal(err) } _, err = f.Write([]byte(file.Body)) if err != nil { log.Fatal(err) }}
}
其實, zip.Create 預設應該是假設檔案名稱採用UTF8編碼, 這樣可以避免不同機器間本地編碼不同導致的解碼的問題.
針對該修改已經提交了 CL54360043, 目前還不清楚是否能夠被接受.
不過比較遺憾的是Win7內建的zip瀏覽器始終是忽略該欄位的(始終用本地編碼處理).