Answers:
目前,tar使用UTF编码文件名
实际上,tar根本不编码/解码文件名,它只是将它们原样复制到文件系统之外。如果您的语言环境是基于UTF-8(在许多现代Linux发行版中),则将是UTF-8。不幸的是,Windows机器的系统代码页从来都不是UTF-8,因此,除非在诸如WinRAR之类的允许更改字符集的工具上使用,否则名称总是会被修饰。
因此,不可能创建具有非ASCII文件名的ZIP文件,该文件可以在不同国家的Windows版本及其内置的压缩文件夹支持中使用。
tar和zip格式的缺点是没有固定或提供的编码信息,因此非ASCII字符将始终不可移植。如果您需要非ASCII存档格式,则必须使用一种较新的格式,例如最近的7z或rar。不幸的是,这些仍然很奇怪。在7zip中,您需要进行-mcu
切换,除非rar检测到代码页中未包含的字符,否则rar仍然不会使用UTF-8。
基本上,这是一个可怕的烂摊子,如果您可以避免分发包含具有非ASCII字符的文件名的档案,那么情况会更好。
这是我编写的一个简单的Python脚本,用于在Windows上的UNIX中解压缩tar文件:
import tarfile
archive_name = "archive_name.tar"
def recover(name):
return unicode(name, 'utf-8')
tar = tarfile.open(name=archive_name, mode='r', bufsize=16*1024)
updated = []
for m in tar.getmembers():
m.name = recover(m.name)
updated.append(m)
tar.extractall(members=updated)
tar.close()
此问题已解决,在Linux中使用了默认值tar
(GNU tar),--format=posix
在创建文件时添加了参数。
例如:
tar --format=posix -cf
在Windows中,要提取文件,请使用bsdtar。
在https://lists.gnu.org/archive/html/bug-tar/2005-02/msg00018.html中写入(自2005年开始!):
>我在ChangeLog中阅读了有关支持UTF-8的内容。这是什么
意思?
>我发现无法创建
在不同区域之间可以互换的档案。当以POSIX.1-2001格式(tar --format = posix或--format = pax)创建档案时,tar将文件名从当前语言环境转换为UTF-8,然后将其存储在档案中。提取时,执行相反的操作。
PS不用键入,--format=posix
您可以键入-H pax
,它更短。
我在解压缩tar
和zip
从Windows用户那里收到的文件时遇到问题。尽管我没有回答“如何创建将起作用的归档文件”的问题,但是以下脚本可帮助正确解压缩tar
和归档zip
文件,而不管原始操作系统如何。
警告:一个具有调谐源手动编码(cp1251
,cp866
在下面的示例)。命令行选项将来可能是一个很好的解决方案。
柏油:
#!/usr/bin/env python
import tarfile
import codecs
import sys
def recover(name):
return codecs.decode(name, 'cp1251')
for tar_filename in sys.argv[1:]:
tar = tarfile.open(name=tar_filename, mode='r', bufsize=16*1024)
updated = []
for m in tar.getmembers():
m.name = recover(m.name)
updated.append(m)
tar.extractall(members=updated)
tar.close()
压缩:
#!/usr/bin/env python
import zipfile
import os
import codecs
import sys
def recover(name):
return codecs.decode(name, 'cp866')
for filename in sys.argv[1:]:
archive = zipfile.ZipFile(filename, 'r')
infolist = archive.infolist()
for i in infolist:
f = recover(i.filename)
print f
if f.endswith("/"):
os.makedirs(os.path.dirname(f))
else:
open(f, 'w').write(archive.read(i))
archive.close()
UPD 2018-01-02:我使用chardet
包来猜测原始数据块的正确编码。现在,脚本可以在我所有的不良档案以及好的档案中立即使用。
注意事项:
chardet
不适用于普通的unicode对象)。最终版本:
#!/usr/bin/env python2
# coding=utf-8
import zipfile
import os
import codecs
import sys
import chardet
def make_encoding_normalizer(txt):
u'''
Takes raw data and returns function to normalize encoding of the data.
* `txt` is either unicode or raw bytes;
* `chardet` library is used to guess the correct encoding.
>>> n_unicode = make_encoding_normalizer(u"Привет!")
>>> print n_unicode(u"День добрый")
День добрый
>>> n_cp1251 = make_encoding_normalizer(u"Привет!".encode('cp1251'))
>>> print n_cp1251(u"День добрый".encode('cp1251'))
День добрый
>>> type(n_cp1251(u"День добрый".encode('cp1251')))
<type 'unicode'>
'''
if isinstance(txt, unicode):
return lambda text: text
enc = chardet.detect(txt)['encoding']
return lambda file_name: codecs.decode(file_name, enc)
for filename in sys.argv[1:]:
archive = zipfile.ZipFile(filename, 'r')
infolist = archive.infolist()
probe_txt = "\n".join(i.filename for i in infolist)
normalizer = make_encoding_normalizer(probe_txt)
for i in infolist:
print i.filename
f = normalizer(i.filename)
print f
dirname = os.path.dirname(f)
if dirname:
assert os.path.abspath(dirname).startswith(os.path.abspath(".")), \
"Security violation"
if not os.path.exists(dirname):
os.makedirs(dirname)
if not f.endswith("/"):
open(f, 'w').write(archive.read(i))
archive.close()
if __name__ == '__main__' and len(sys.argv) == 1:
# Hack for Python 2.x to support unicode source files as doctest sources.
reload(sys)
sys.setdefaultencoding("UTF-8")
import doctest
doctest.testmod()
print "If there are no messages above, the script passes all tests."
POSIX-1.2001指定了TAR如何使用UTF-8。
从2007年开始,PKZIP APPNOTE.TXT(http://www.pkware.com/documents/casestudies/APPNOTE.TXT)中的changelog版本6.3.0 指定了ZIP如何使用UTF-8。
只有哪种工具能够正确支持这些标准,这仍然是一个悬而未决的问题。