Bagaimana saya bisa membuat zip / tgz di Linux sehingga Windows memiliki nama file yang tepat?

26

Saat ini, tar -zcf arch.tgz files/*menyandikan nama file di UTF, sehingga pengguna Windows melihat semua karakter manja dalam nama file yang bukan bahasa Inggris , dan tidak dapat melakukan apa-apa dengan itu.

zip -qq -r arch.zip files/* memiliki perilaku yang sama.

Bagaimana saya bisa membuat arsip zip / tgz sehingga ketika pengguna Windows mengekstraknya, semua nama file akan dikodekan dengan benar?

kolypto
sumber

Jawaban:

24

Saat ini, tar menyandikan nama file di UTF

Sebenarnya tar tidak menyandikan / mendekode nama file sama sekali, itu hanya menyalinnya dari sistem file apa adanya. Jika lokal Anda berbasis UTF-8 (seperti pada banyak distro Linux modern), itu akan menjadi UTF-8. Sayangnya sistem codepage dari kotak Windows tidak pernah UTF-8, jadi nama-nama akan selalu hancur kecuali pada alat-alat seperti WinRAR yang memungkinkan charset yang digunakan untuk diubah.

Jadi tidak mungkin membuat file ZIP dengan nama file non-ASCII yang bekerja di berbagai rilis Windows dari berbagai negara dan dukungan folder terkompresi bawaan mereka.

Ini adalah kelemahan dari tar dan format zip yang tidak ada informasi pengkodean tetap atau disediakan, sehingga karakter non-ASCII akan selalu non-portabel. Jika Anda memerlukan format arsip non-ASCII, Anda harus menggunakan salah satu format yang lebih baru, seperti 7z atau rar terbaru. Sayangnya ini masih miring; di 7zip Anda memerlukan -mcusakelar, dan rar masih tidak akan menggunakan UTF-8 kecuali mendeteksi karakter yang tidak ada dalam codepage.

Pada dasarnya ini adalah kekacauan yang mengerikan dan jika Anda dapat menghindari mendistribusikan arsip yang berisi nama file dengan karakter non-ASCII Anda akan jauh lebih baik.

bobince
sumber
Terima kasih banyak! Sayangnya, sebagian besar pengguna tidak tahu apa-apa tentang 7z, dan rar adalah hak milik :(
kolypto
Ya, itu masalah. ZIP sejauh ini merupakan solusi yang paling bermanfaat bagi pengguna, karena semua OS modern memiliki dukungan UI asli yang bagus untuk itu. Sayangnya masalah charset tidak benar-benar dipecahkan hari ini di ZIP (dan bahkan dalam format arsip lain itu masih merepotkan).
bobince
25

Berikut ini adalah skrip Python sederhana yang saya tulis untuk membongkar file tar dari UNIX di 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()
Alexei Osipov
sumber
Luar biasa! skrip ini membantu saya mengonversi file tar enkode EUC-JP yang dibuat pada server Solaris lama.
wm_eddie
Pak, Anda menyelamatkan hidup saya. Tuhan memberkati Anda :)
user1576772
8

Masalahnya, menggunakan Linux default tar(GNU tar), dipecahkan ... menambahkan --format=posixparameter saat membuat file.

Sebagai contoh:
tar --format=posix -cf

Di Windows, untuk mengekstrak file, saya menggunakan bsdtar .

Dalam https://lists.gnu.org/archive/html/bug-tar/2005-02/msg00018.html ditulis (sejak 2005 !!):

> Saya membaca sesuatu di ChangeLog tentang UTF-8 yang didukung. Apa artinya
ini?
> Saya tidak menemukan cara untuk membuat arsip yang dapat dipertukarkan
> antara berbagai lokasi.

Saat membuat arsip dalam format POSIX.1-2001 (tar --format = posix atau --format = pax), tar mengubah nama file dari lokal saat ini ke UTF-8 dan kemudian menyimpannya dalam arsip. Saat mengekstraksi, operasi terbalik dilakukan.

PS Alih-alih mengetik --format=posixAnda bisa mengetik -H pax, yang lebih pendek.

Sys
sumber
5

Saya yakin Anda mengalami masalah dengan format wadah Zip itu sendiri. Tar mungkin menderita masalah yang sama.

Gunakan format arsip 7zip ( .7z) atau RAR ( .rar) sebagai gantinya. Keduanya tersedia untuk Windows dan Linux; yang p7ziplunak menangani kedua format.

Aku hanya diuji menciptakan .7z, .rar, .zip, dan .tarfile di kedua WinXP dan Debian 5, dan .7zdan .rarmenyimpan file / mengembalikan nama file dengan benar sementara .zipdan .tarfile tidak. Tidak masalah sistem mana yang digunakan untuk membuat arsip pengujian.

quack quixote
sumber
5

Saya punya masalah dengan membongkar tardan zipfile yang saya terima dari pengguna Windows. Meskipun saya tidak menjawab pertanyaan "bagaimana cara membuat arsip yang akan berfungsi", skrip di bawah ini membantu membongkar tardan zipfile dengan benar terlepas dari OS aslinya.

PERINGATAN: kita harus menyetel sumber encoding secara manual ( cp1251, cp866dalam contoh di bawah). Opsi baris perintah mungkin merupakan solusi yang baik di masa depan.

Ter:

#!/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 : Saya menggunakan chardetpaket untuk menebak pengkodean yang benar dari data mentah. Sekarang skrip bekerja di luar kotak pada semua arsip buruk saya, serta yang baik.

Hal yang perlu diperhatikan:

  1. Semua nama file diekstraksi dan digabungkan ke dalam string tunggal untuk membuat bagian teks yang lebih besar untuk mesin menebak enkode. Ini berarti bahwa beberapa nama file yang dikacaukan dengan cara yang berbeda masing-masing dapat merusak dugaan.
  2. Jalur cepat khusus digunakan untuk menangani teks unicode yang baik ( chardettidak berfungsi dengan objek unicode normal).
  3. Dokumen ditambahkan untuk menguji dan menunjukkan bahwa normalizer mengenali penyandian pada string yang cukup pendek.

Versi akhir:

#!/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
sumber
Terima kasih untuk programmu! Menyesal, program Zip tidak bekerja di bawah Python 3, tetapi ia bekerja di bawah Python 2.
beroal
@beroal, saya memperbarui skrip. Sekarang ia menggunakan mesin yang dikembangkan oleh Mozilla untuk Firefox untuk mendeteksi secara otomatis pengkodean.
dmitry_romanov
4

POSIX-1.2001 menentukan bagaimana TAR menggunakan UTF-8.

Pada 2007, changelog versi 6.3.0 di PKZIP APPNOTE.TXT ( http://www.pkware.com/documents/casestudies/APPNOTE.TXT ) menetapkan cara ZIP menggunakan UTF-8.

Hanya alat mana yang mendukung standar ini dengan benar, yang tetap menjadi pertanyaan terbuka.

Damjan
sumber