Как я могу создать zip / tgz в Linux, чтобы в Windows были правильные имена файлов?

26

В настоящее время tar -zcf arch.tgz files/*кодирует имена файлов в UTF, поэтому пользователи Windows видят все испорченные символы в именах файлов, которые не являются английскими , и ничего не могут с этим поделать.

zip -qq -r arch.zip files/* имеет такое же поведение.

Как я могу создать архив zip / tgz, чтобы при извлечении пользователями Windows в нем были правильно закодированы все имена файлов?

kolypto
источник

Ответы:

24

В настоящее время tar кодирует имена файлов в UTF

На самом деле tar вообще не кодирует / не декодирует имена файлов, он просто копирует их из файловой системы как есть. Если ваша локаль основана на UTF-8 (как во многих современных дистрибутивах Linux), это будет UTF-8. К сожалению, системная кодовая страница Windows-блока никогда не является UTF-8, поэтому имена всегда будут искажены, за исключением таких инструментов, как WinRAR, которые позволяют изменять кодировку.

Таким образом, невозможно создать ZIP-файл с именами, отличными от ASCII, которые работают в разных версиях Windows и поддерживают встроенные сжатые папки.

Недостатком форматов tar и zip является отсутствие фиксированной или предоставленной информации о кодировке, поэтому символы, не входящие в ASCII, всегда будут непереносимыми. Если вам нужен формат архива не ASCII, вам придется использовать один из более новых форматов, например, последний 7z или rar. К сожалению, они все еще шаткие; в 7zip вам нужен -mcuпереключатель, и rar по-прежнему не будет использовать UTF-8, если не обнаружит символы, отсутствующие в кодовой странице.

По сути, это ужасный беспорядок, и если вы сможете избежать распространения архивов, содержащих имена файлов с не-ASCII-символами, вам будет намного лучше.

bobince
источник
Большое спасибо! К сожалению, большинство пользователей ничего не знают о 7z, а rar является проприетарным :(
kolypto
Да, это проблема. ZIP, безусловно, является наиболее удобным решением для пользователей, поскольку все современные ОС имеют приятную встроенную поддержку пользовательского интерфейса. К сожалению, проблема с кодировкой не очень разрешима сегодня в ZIP (и даже в других форматах архивов это все еще проблематично).
bobince
25

Вот простой скрипт Python, который я написал для распаковки tar-файлов из UNIX в Windows:

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()
Алексей Осипов
источник
Потрясающе! Этот скрипт помог мне конвертировать EUC-JP-кодированный tar-файл, созданный на старом сервере Solaris.
wm_eddie
Сэр, вы спасли мою жизнь. Благословит вас Бог :)
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. Тар может страдать от той же проблемы.

Используйте 7zip ( .7z) или RAR ( .rar) форматы архивов вместо. Оба доступны для Windows и Linux; p7zipпрограммное обеспечение обрабатывает оба формата.

Я просто проверял , создающего .7z, .rar, .zipи .tarфайлы на обоих WinXP и Debian 5, а .7zи .rarхранения файлов / восстановить имена файлов правильно в то время как .zipи .tarфайлы не делают. Неважно, какая система используется для создания тестового архива.

шарлатан
источник
5

У меня были проблемы с распаковкой 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()

Zip:

#!/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. Тесты добавляются для тестирования и демонстрации того, что нормализатор распознает любую кодировку на достаточно коротких строках.

Окончательный версия:

#!/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."
dmitry_romanov
источник
Спасибо за ваши программы! К сожалению, программа Zip не работает под Python 3, но она работает под Python 2.
бероал
@beroal, я обновил скрипт. Теперь он использует движок, разработанный Mozilla для Firefox, для автоматического определения кодировки.
dmitry_romanov
4

В POSIX-1.2001 указано, как TAR использует UTF-8.

Начиная с 2007 года в версии журнала изменений 6.3.0 в приложении PKZIP APPNOTE.TXT ( http://www.pkware.com/documents/casestudies/APPNOTE.TXT ) указано, как ZIP использует UTF-8.

Только то, какие инструменты поддерживают эти стандарты должным образом, остается открытым вопросом.

Damjan
источник