bob*_*nce 25
目前,tar 以 UTF 编码文件名
实际上 tar 根本不编码/解码文件名,它只是按原样将它们从文件系统中复制出来。如果您的语言环境是基于 UTF-8 的(如在许多现代 Linux 发行版中),那将是 UTF-8。不幸的是,Windows 机器的系统代码页永远不会是 UTF-8,因此名称将始终被修改,除非在 WinRAR 等允许更改字符集的工具上使用。
因此,不可能创建具有非 ASCII 文件名的 ZIP 文件,该文件名适用于不同国家/地区的 Windows 版本及其内置的压缩文件夹支持。
tar 和 zip 格式的一个缺点是没有固定或提供的编码信息,因此非 ASCII 字符总是不可移植的。如果您需要非 ASCII 存档格式,则必须使用较新的格式之一,例如最近的 7z 或 rar。不幸的是,这些仍然不稳定;在 7zip 中,您需要-mcu开关,而 rar 仍然不会使用 UTF-8,除非它检测到不在代码页中的字符。
基本上这是一个可怕的混乱,如果你能避免分发包含非 ASCII 字符的文件名的档案,你会好得多。
Ale*_*pov 25
这是我编写的一个简单的 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()
Run Code Online (Sandbox Code Playgroud)
在 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更短的 ,而不是输入。
小智 5
POSIX-1.2001 指定了 TAR 如何使用 UTF-8。
截至 2007 年,PKZIP APPNOTE.TXT ( http://www.pkware.com/documents/casestudies/APPNOTE.TXT ) 中的变更日志版本 6.3.0 指定了 ZIP 如何使用 UTF-8。
只是哪些工具能够正确支持这些标准,这仍然是一个悬而未决的问题。
我在解压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()
Run Code Online (Sandbox Code Playgroud)
压缩:
#!/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()
Run Code Online (Sandbox Code Playgroud)
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."
Run Code Online (Sandbox Code Playgroud)