如何在Linux中创建zip / tgz,以便Windows具有正确的文件名?


26

当前,tar -zcf arch.tgz files/*文件名以UTF编码,因此Windows用户会看到所有损坏的字符都不是英语的文件名,因此无法执行任何操作。

zip -qq -r arch.zip files/* 具有相同的行为。

如何创建zip / tgz存档,以便Windows用户提取该存档时,所有文件名都正确编码?

Answers:


24

目前,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字符的文件名的档案,那么情况会更好。


万分谢意!不幸的是,大多数用户对7z一无所知,而rar是专有的:(
kolypto

是的,这是一个问题。到目前为止,ZIP是用户最可用的解决方案,因为所有现代OS都对其提供了很好的本机UI支持。不幸的是,今天的ZIP字符集问题并不能真正解决(甚至在其他存档格式中也很麻烦)。
bobince

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()

太棒了!该脚本帮助我转换了在旧Solaris服务器上创建的EUC-JP编码的tar文件。
wm_eddie 2010年

先生,您救了我的命。上帝保佑你:)
user1576772

8

此问题已解决,在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

我相信您在使用Zip容器格式本身时遇到了问题。Tar可能会遇到相同的问题。

请改用7zip.7z)或RAR(.rar)存档格式。两者都可用于Windows和Linux。该p7zip软件可以处理两种格式。

我只是测试创建.7z.rar.zip,和.tar双方的WinXP和Debian 5,并且这些文件.7z.rar文件存储/文件名,而恢复正常.zip.tar文件不。使用哪个系统创建测试档案都没有关系。


5

我在解压缩tarzip从Windows用户那里收到的文件时遇到问题。尽管我没有回答“如何创建将起作用的归档文件”的问题,但是以下脚本可帮助正确解压缩tar和归档zip文件,而不管原始操作系统如何。

警告:一个具有调谐源手动编码(cp1251cp866在下面的示例)。命令行选项将来可能是一个很好的解决方案。

柏油:

#!/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包来猜测原始数据块的正确编码。现在,脚本可以在我所有的不良档案以及好的档案中立即使用。

注意事项:

  1. 提取所有文件名并将其合并为单个字符串,以为编码猜测引擎制作更大的文本部分。这意味着很少有文件名以不同的方式拧入,每个文件名都可能破坏猜测。
  2. 特殊的快速路径用于处理良好的unicode文本(chardet不适用于普通的unicode对象)。
  3. Doctests被添加到测试中,并证明规范化器可以识别合理的短字符串上的任何编码。

最终版本:

#!/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."

谢谢您的节目!令人遗憾的是,Zip程序不Python的3下工作,但它工作的Python 2下
beroal

@beroal,我更新了脚本。现在,它使用Mozilla为Firefox开发的引擎来自动检测编码。
dmitry_romanov '18

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.