Como posso criar um zip / tgz no Linux para que o Windows tenha nomes de arquivos adequados?

26

Atualmente, tar -zcf arch.tgz files/*codifica nomes de arquivos em UTF, para que os usuários do Windows vejam todos os caracteres estragados em nomes de arquivos que não sejam ingleses e não possam fazer nada com isso.

zip -qq -r arch.zip files/* tem o mesmo comportamento.

Como posso criar um arquivo zip / tgz para que, quando os usuários do Windows extraiam, todos os nomes de arquivos sejam codificados corretamente?

Kolypto
fonte

Respostas:

24

Atualmente, o tar codifica os nomes de arquivos em UTF

Na verdade, o tar não codifica / decodifica nomes de arquivos, simplesmente os copia do sistema de arquivos como está. Se o seu código do idioma for baseado em UTF-8 (como em muitas distros modernas do Linux), será UTF-8. Infelizmente, a página de código do sistema de uma caixa do Windows nunca é UTF-8; portanto, os nomes sempre serão alterados, exceto em ferramentas como o WinRAR, que permitem alterar o conjunto de caracteres usado.

Portanto, é impossível criar um arquivo ZIP com nomes de arquivos não ASCII que funcionem nos lançamentos do Windows em diferentes países e em seu suporte incorporado a pastas compactadas.

É uma falta dos formatos tar e zip que não há informações de codificação fixas ou fornecidas, portanto, caracteres não ASCII sempre serão não portáveis. Se você precisar de um formato de arquivo não ASCII, precisará usar um dos formatos mais recentes, como o 7z recente ou o rar. Infelizmente estes ainda são vacilantes; no 7zip, você precisa da -mcuopção, e o rar ainda não usará UTF-8, a menos que detecte caracteres que não estão na página de códigos.

Basicamente, é uma bagunça horrível e, se você puder evitar a distribuição de arquivos contendo nomes de arquivos com caracteres não ASCII, ficará muito melhor.

bobince
fonte
Ótimo, obrigado! Infelizmente, a maioria dos usuários não sabe nada sobre 7z, RAR e é proprietária :(
kolypto
Sim, é um problema. O ZIP é de longe a solução mais utilizável para os usuários, pois todos os sistemas operacionais modernos têm um bom suporte nativo à interface do usuário. Infelizmente, o problema do charset não é realmente solucionável atualmente no ZIP (e mesmo em outros formatos de arquivo ainda é problemático).
22990
25

Aqui está um script Python simples que eu escrevi para descompactar arquivos tar do UNIX no 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
fonte
Impressionante! esse script me ajudou a converter um arquivo tar codificado em EUC-JP que foi criado em um servidor Solaris antigo.
Wm_eddie
Senhor, você salvou minha vida. Deus te abençoe :)
user1576772
8

O problema, usando no Linux o padrão tar(tar GNU), foi resolvido ... adicionando o --format=posixparâmetro ao criar o arquivo.

Por exemplo:
tar --format=posix -cf

No Windows, para extrair os arquivos, eu uso o bsdtar .

Em https://lists.gnu.org/archive/html/bug-tar/2005-02/msg00018.html , está escrito (desde 2005 !!):

> Li algo no ChangeLog sobre o suporte a UTF-8. O que
isso significa?
Não encontrei maneira de criar um arquivo que fosse intercambiável
entre diferentes localidades.

Ao criar arquivos no formato POSIX.1-2001 (tar --format = posix ou --format = pax), o tar converte nomes de arquivos dos locais atuais em UTF-8 e os armazena no arquivo morto. Ao extrair, a operação reversa é executada.

PS Em vez de digitar, --format=posixvocê pode digitar -H pax, que é mais curto.

Sys
fonte
5

Acredito que você esteja tendo problemas com o próprio formato do contêiner Zip. O alcatrão pode estar sofrendo do mesmo problema.

Use os formatos de arquivo 7zip ( .7z) ou RAR ( .rar). Ambos estão disponíveis para Windows e Linux; o p7zipsoftware lida com os dois formatos.

Eu apenas testada criando .7z, .rar, .zip, e .tararquivos no WinXP e Debian 5, eo .7ze .rararmazenar arquivos / restaurar nomes de arquivo corretamente, enquanto o .zipe .tararquivos não. Não importa qual sistema é usado para criar o arquivo de teste.

charlatão quixote
fonte
5

Eu tive problemas com a descompactação tare os ziparquivos que recebo dos usuários do Windows. Embora eu não responda à pergunta "como criar o arquivo que funcionará", os scripts abaixo ajudam a descompactar tare ziparquivos corretamente, independentemente do sistema operacional original.

ATENÇÃO: uma tem a sintonizar a codificação da fonte manualmente ( cp1251, cp866nos exemplos abaixo). As opções de linha de comando podem ser uma boa solução no futuro.

Alcatrão:

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

Fecho eclair:

#!/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 : Eu uso o chardetpacote para adivinhar a codificação correta do pedaço bruto de dados. Agora, o script funciona imediatamente em todos os meus arquivos defeituosos, bem como nos bons.

Coisas a serem observadas:

  1. Todos os nomes de arquivos são extraídos e mesclados na cadeia única para criar uma parte maior do texto para o mecanismo de adivinhação de codificação. Isso significa que poucos nomes de arquivos estragados de uma maneira diferente, cada um pode estragar o palpite.
  2. O atalho especial foi usado para manipular um bom texto unicode ( chardetnão funciona com um objeto unicode normal).
  3. Os testes são adicionados para testar e demonstrar que o normalizador reconhece qualquer codificação em cadeias razoavelmente curtas.

Versão final:

#!/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
fonte
Obrigado por seus programas! Lamentavelmente, o programa Zip não funciona sob Python 3, mas trabalha sob Python 2.
beroal
@beroal, atualizei o script. Agora, ele usa o mecanismo desenvolvido pela Mozilla para Firefox para detectar automaticamente a codificação.
dmitry_romanov
4

O POSIX-1.2001 especificou como o TAR usa o UTF-8.

A partir de 2007, o changelog versão 6.3.0 no PKZIP APPNOTE.TXT ( http://www.pkware.com/documents/casestudies/APPNOTE.TXT ) especificou como o ZIP usa o UTF-8.

São apenas as ferramentas que suportam esses padrões adequadamente, que permanece uma questão em aberto.

damjan
fonte