Jak mogę utworzyć zip / tgz w systemie Linux, aby system Windows miał prawidłowe nazwy plików?


26

Obecnie tar -zcf arch.tgz files/*koduje nazwy plików w UTF, więc użytkownicy Windows widzą wszystkie znaki zepsute w nazwach plików, które nie są angielskie , i nie mogą nic z tym zrobić.

zip -qq -r arch.zip files/* ma takie samo zachowanie.

Jak mogę utworzyć archiwum zip / tgz, aby po rozpakowaniu przez użytkowników systemu Windows wszystkie nazwy plików były poprawnie zakodowane?

Odpowiedzi:


24

Obecnie tar koduje nazwy plików w UTF

W rzeczywistości tar w ogóle nie koduje / dekoduje nazw plików, po prostu kopiuje je z systemu plików bez zmian. Jeśli twoje ustawienia regionalne są oparte na UTF-8 (jak w wielu współczesnych dystrybucjach Linuksa), będzie to UTF-8. Niestety systemowa strona kodowa okna systemu Windows nigdy nie ma formatu UTF-8, więc nazwy zawsze będą zniekształcone, z wyjątkiem narzędzi takich jak WinRAR, które pozwalają na zmianę zestawu znaków.

Dlatego niemożliwe jest utworzenie pliku ZIP z nazwami plików spoza ASCII, które działają w wersjach systemu Windows w różnych krajach i mają wbudowaną obsługę folderów skompresowanych.

Wadą formatów tar i zip jest to, że nie ma ustalonych lub dostarczonych informacji o kodowaniu, więc znaki spoza ASCII zawsze będą nieprzenośne. Jeśli potrzebujesz formatu archiwum spoza ASCII, musisz użyć jednego z nowszych formatów, takich jak najnowszy 7z lub rar. Niestety są one nadal dziwne; w 7zip potrzebujesz -mcuprzełącznika, a rar nadal nie będzie używał UTF-8, chyba że wykryje znaki spoza strony kodowej.

Zasadniczo jest to okropny bałagan i jeśli możesz uniknąć dystrybucji archiwów zawierających nazwy plików ze znakami spoza ASCII, będziesz znacznie lepiej.


Wielkie dzięki! Niestety większość użytkowników nic nie wie o 7z, a rar jest zastrzeżony :(
kolypto

Tak, to problem. ZIP jest zdecydowanie najbardziej użytecznym rozwiązaniem dla użytkowników, ponieważ wszystkie nowoczesne systemy operacyjne mają ładną natywną obsługę interfejsu użytkownika. Niestety, problem kodowania znaków nie jest dziś tak naprawdę do rozwiązania w ZIP (i nawet w innych formatach archiwów jest nadal kłopotliwy).
bobince

25

Oto prosty skrypt w języku Python, który napisałem w celu rozpakowania plików tar z systemu UNIX w systemie 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()

Niesamowite! ten skrypt pomógł mi przekonwertować plik tar zakodowany w EUC-JP, który został utworzony na starym serwerze Solaris.
wm_eddie

Sir, uratowałeś mi życie. Niech Bóg cię błogosławi :)
user1576772

8

Problem, przy użyciu domyślnego systemu Linux tar(tar GNU), został rozwiązany ... dodając --format=posixparametr podczas tworzenia pliku.

Na przykład:
tar --format=posix -cf

W systemie Windows do wyodrębnienia plików używam bsdtar .

W https://lists.gnu.org/archive/html/bug-tar/2005-02/msg00018.html jest napisane (od 2005 roku !!):

> Przeczytałem coś w Dzienniku zmian na temat obsługiwanego UTF-8. Co
to znaczy?
> Nie znalazłem sposobu na utworzenie archiwum, które byłoby wymienne
> między różnymi lokalizacjami.

Podczas tworzenia archiwów w formacie POSIX.1-2001 (tar --format = posix lub --format = pax), tar konwertuje nazwy plików z bieżących ustawień narodowych na UTF-8, a następnie przechowuje je w archiwum. Podczas rozpakowywania wykonywana jest operacja odwrotna.

PS Zamiast pisać --format=posix, możesz pisać -H pax, co jest krótsze.


5

Uważam, że masz problemy z samym formatem kontenera Zip. Smoła może cierpieć na ten sam problem.

Zamiast tego użyj formatu archiwum 7zip ( .7z) lub RAR ( .rar). Oba są dostępne dla systemu Windows i Linux; p7zipoprogramowanie obsługuje oba formaty.

I właśnie przetestowane wykreowanie .7z, .rar, .ziporaz .tarpliki na obu WinXP i Debian 5, a .7zi .rarprzechowywać pliki / przywracania prawidłowej nazwy plików podczas gdy .zipi .tarpliki nie. Nie ma znaczenia, który system zostanie użyty do utworzenia archiwum testowego.


5

Miałem problemy z rozpakowywaniem tari zipplikami otrzymywanymi od użytkowników systemu Windows. Chociaż nie odpowiadam na pytanie „jak utworzyć archiwum, które będzie działać”, poniższe skrypty pomagają poprawnie rozpakować pliki tari zipniezależnie od oryginalnego systemu operacyjnego.

UWAGA: trzeba dostosować źródło kodujący ręcznie ( cp1251, cp866w przykładach poniżej). Opcje linii poleceń mogą być dobrym rozwiązaniem w przyszłości.

Smoła:

#!/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()

Zamek błyskawiczny:

#!/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 : Używam chardetpakietu do odgadnięcia poprawnego kodowania surowej porcji danych. Teraz skrypt działa od razu na wszystkich moich złych archiwach, a także na dobrych.

Ważne uwagi:

  1. Wszystkie nazwy plików są wyodrębniane i łączone w pojedynczy ciąg, aby utworzyć większy fragment tekstu dla mechanizmu zgadywania kodowania. Oznacza to, że kilka nazw plików wkręconych w inny sposób może zepsuć domysły.
  2. Specjalna szybka ścieżka została wykorzystana do obsługi dobrego tekstu Unicode ( chardetnie działa z normalnym obiektem Unicode).
  3. Testy dokumentów są dodawane w celu przetestowania i wykazania, że ​​normalizator rozpoznaje kodowanie na stosunkowo krótkich ciągach.

Wersja ostateczna:

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

Dziękujemy za twoje programy! Niestety program Zip nie działa w Pythonie 3, ale działa w Pythonie 2
beroal

@beroal, zaktualizowałem skrypt. Teraz korzysta z silnika opracowanego przez Mozillę dla Firefoksa do automatycznego wykrywania kodowania.
dmitry_romanov

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.