Comment puis-je créer un zip / tgz sous Linux de telle sorte que Windows ait des noms de fichiers appropriés?

26

Actuellement, tar -zcf arch.tgz files/*code les noms de fichiers en UTF, de sorte que les utilisateurs de Windows voient tous les caractères gâtés dans des noms de fichiers qui ne sont pas en anglais et ne peuvent rien faire avec.

zip -qq -r arch.zip files/* a le même comportement.

Comment puis-je créer une archive zip / tgz pour que lorsque les utilisateurs de Windows l'extraient, tous les noms de fichiers soient encodés correctement?

kolypto
la source

Réponses:

24

Actuellement, tar code les noms de fichiers en UTF

En fait, tar ne code pas / décode les noms de fichiers, il les copie simplement du système de fichiers tels quels. Si votre environnement local est basé sur UTF-8 (comme dans de nombreuses distributions Linux modernes), ce sera UTF-8. Malheureusement, la page de codes système d'une boîte Windows n'est jamais UTF-8, donc les noms seront toujours modifiés, sauf sur des outils tels que WinRAR qui permettent de modifier le jeu de caractères utilisé.

Il est donc impossible de créer un fichier ZIP avec des noms de fichiers non ASCII qui fonctionnent dans les versions de Windows de différents pays et leur prise en charge intégrée des dossiers compressés.

Il y a une lacune des formats tar et zip, car il n'y a pas d'informations de codage fixes ou fournies, donc les caractères non ASCII seront toujours non portables. Si vous avez besoin d'un format d'archive non ASCII, vous devrez utiliser l'un des formats les plus récents, tels que 7z récent ou rar. Malheureusement, ce sont toujours loufoques; dans 7zip, vous avez besoin du -mcucommutateur, et rar n'utilisera toujours pas UTF-8 à moins qu'il ne détecte des caractères absents de la page de codes.

Fondamentalement, c'est un désordre horrible et si vous pouvez éviter de distribuer des archives contenant des noms de fichiers avec des caractères non ASCII, vous serez beaucoup mieux.

bobince
la source
Grand merci! Malheureusement, la plupart des utilisateurs ne savent rien de 7z, et rar est propriétaire :(
kolypto
Ouais, c'est un problème. ZIP est de loin la solution la plus utilisable pour les utilisateurs, car tous les systèmes d'exploitation modernes ont une bonne prise en charge native de l'interface utilisateur. Malheureusement, le problème du jeu de caractères n'est pas vraiment résolu aujourd'hui dans ZIP (et même dans d'autres formats d'archives, il est toujours gênant).
bobince
25

Voici un simple script Python que j'ai écrit pour décompresser les fichiers tar d'UNIX sous 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
la source
Impressionnant! ce script m'a aidé à convertir un fichier tar encodé EUC-JP créé sur un ancien serveur Solaris.
wm_eddie
Monsieur, vous m'avez sauvé la vie. Que Dieu vous bénisse :)
user1576772
8

Le problème, en utilisant Linux par défaut tar(GNU tar), est résolu ... en ajoutant le --format=posixparamètre lors de la création du fichier.

Par exemple:
tar --format=posix -cf

Sous Windows, pour extraire les fichiers, j'utilise bsdtar .

Dans https://lists.gnu.org/archive/html/bug-tar/2005-02/msg00018.html il est écrit (depuis 2005 !!):

> J'ai lu quelque chose dans le ChangeLog sur la prise en charge de l'UTF-8. Qu'est-
ce que cela signifie?
> Je n'ai trouvé aucun moyen de créer une archive qui serait interchangeable
entre différents paramètres régionaux.

Lors de la création d'archives au format POSIX.1-2001 (tar --format = posix ou --format = pax), tar convertit les noms de fichiers des paramètres régionaux actuels en UTF-8, puis les stocke dans l'archive. Lors de l'extraction, l'opération inverse est effectuée.

PS Au lieu de taper, --format=posixvous pouvez taper -H pax, ce qui est plus court.

Sys
la source
5

Je crois que vous rencontrez des problèmes avec le format de conteneur Zip lui-même. Le goudron souffre peut-être du même problème.

Utilisez plutôt les formats d'archive 7zip ( .7z) ou RAR ( .rar). Les deux sont disponibles pour Windows et Linux; le p7ziplogiciel gère les deux formats.

Je viens de tester créant .7z, .rar, .zipet .tarfichiers à la fois Windows XP et Debian 5, et .7zet .rarfichiers de banque / restaurer correctement les noms de fichiers alors que les .zipet les .tarfichiers ne le font pas. Peu importe le système utilisé pour créer l'archive de test.

Quack Quichotte
la source
5

J'ai eu des problèmes avec le déballage taret les zipfichiers que je reçois des utilisateurs de Windows. Bien que je ne réponde pas à la question "comment créer l'archive qui fonctionnera", les scripts ci-dessous aident à décompresser taret à classer les zipfichiers correctement quel que soit le système d'exploitation d'origine.

ATTENTION: il faut régler la source codant manuellement ( cp1251, cp866dans les exemples ci - dessous). Les options de ligne de commande peuvent être une bonne solution à l'avenir.

Le goudron:

#!/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 *: français:

#!/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 : J'utilise le chardetpackage pour deviner l'encodage correct du bloc de données brut. Maintenant, le script fonctionne hors de la boîte sur toutes mes mauvaises archives, ainsi que les bonnes.

À noter:

  1. Tous les noms de fichiers sont extraits et fusionnés dans la chaîne unique pour faire une plus grande partie du texte pour le moteur de devinette d'encodage. Cela signifie que peu de noms de fichiers vissés d'une manière différente peuvent gâcher la supposition.
  2. Un raccourci spécial a été utilisé pour gérer un bon texte Unicode ( chardetne fonctionne pas avec un objet Unicode normal).
  3. Des tests sont ajoutés pour tester et démontrer que le normalisateur reconnaît tout encodage sur des chaînes raisonnablement courtes.

Version finale:

#!/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
la source
Merci pour vos programmes! Malheureusement, le programme Zip ne fonctionne pas sous Python 3, mais il fonctionne sous Python 2.
beroal
@beroal, j'ai mis à jour le script. Désormais, il utilise le moteur développé par Mozilla pour Firefox pour détecter automatiquement l'encodage.
dmitry_romanov
4

POSIX-1.2001 a spécifié comment TAR utilise UTF-8.

Depuis 2007, la version 6.3.0 du journal des modifications dans PKZIP APPNOTE.TXT ( http://www.pkware.com/documents/casestudies/APPNOTE.TXT ) spécifiait comment ZIP utilise UTF-8.

Ce n'est que les outils qui prennent correctement en charge ces normes, qui reste une question ouverte.

damjan
la source