¿Cómo puedo crear un zip / tgz en Linux para que Windows tenga los nombres de archivo adecuados?

26

Actualmente, tar -zcf arch.tgz files/*codifica los nombres de archivo en UTF, por lo que los usuarios de Windows ven todos los caracteres estropeados en nombres de archivo que no están en inglés y no pueden hacer nada con él.

zip -qq -r arch.zip files/* Tiene el mismo comportamiento.

¿Cómo puedo crear un archivo zip / tgz para que cuando los usuarios de Windows lo extraigan, todos los nombres de archivo estén codificados correctamente?

Kolypto
fuente

Respuestas:

24

Actualmente, tar codifica los nombres de archivo en UTF

En realidad, tar no codifica / decodifica nombres de archivos, simplemente los copia del sistema de archivos tal como está. Si su configuración regional está basada en UTF-8 (como en muchas distribuciones modernas de Linux), será UTF-8. Desafortunadamente, la página de códigos del sistema de un cuadro de Windows nunca es UTF-8, por lo que los nombres siempre se modificarán, excepto en herramientas como WinRAR que permiten cambiar el juego de caracteres.

Por lo tanto, es imposible crear un archivo ZIP con nombres de archivo que no sean ASCII que funcionen en las versiones de Windows de diferentes países y su compatibilidad con carpetas comprimidas incorporadas.

Es una deficiencia de los formatos tar y zip que no haya información de codificación fija o suministrada, por lo que los caracteres no ASCII siempre serán no portables. Si necesita un formato de archivo que no sea ASCII, deberá usar uno de los formatos más nuevos, como el reciente 7z o rar. Lamentablemente, estos todavía son inestables; en 7zip necesita el -mcuinterruptor, y rar todavía no usará UTF-8 a menos que detecte caracteres que no están en la página de códigos.

Básicamente es un desastre horrible y si puede evitar distribuir archivos que contengan nombres de archivos con caracteres que no sean ASCII, estará mucho mejor.

bobince
fuente
¡Muchas gracias! Desafortunadamente, la mayoría de los usuarios no saben nada sobre 7z, y rar es propietario :(
kolypto
Si, es un problema. ZIP es, con mucho, la solución más útil para los usuarios, ya que todos los sistemas operativos modernos tienen una buena compatibilidad con la interfaz de usuario nativa. Desafortunadamente, el problema del juego de caracteres no puede resolverse realmente en ZIP (e incluso en otros formatos de archivo sigue siendo problemático).
bobince
25

Aquí hay un script simple de Python que he escrito para descomprimir archivos tar de UNIX en 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
fuente
¡Increíble! Este script me ayudó a convertir un archivo tar codificado EUC-JP que se creó en un antiguo servidor Solaris.
wm_eddie
Señor, me salvaste la vida. Dios te bendiga :)
user1576772
8

El problema, al usar en Linux el valor predeterminado tar(GNU tar), se resuelve ... agregando el --format=posixparámetro al crear el archivo.

Por ejemplo:
tar --format=posix -cf

En Windows, para extraer los archivos, uso bsdtar .

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

> Leí algo en ChangeLog acerca de que UTF-8 es compatible. ¿Qué significa
esto?
> No encontré ninguna manera de crear un archivo que fuera intercambiable
> entre diferentes configuraciones regionales.

Al crear archivos en formato POSIX.1-2001 (tar --format = posix o --format = pax), tar convierte los nombres de los archivos de las configuraciones regionales actuales a UTF-8 y luego los almacena en el archivo. Al extraer, se realiza la operación inversa.

PD: en lugar de escribir --format=posix, puede escribir -H pax, que es más corto.

Sys
fuente
5

Creo que tienes problemas con el formato del contenedor Zip. El alquitrán puede estar sufriendo el mismo problema.

Utilice los formatos de archivo 7zip ( .7z) o RAR ( .rar) en su lugar. Ambos están disponibles para Windows y Linux; El p7zipsoftware maneja ambos formatos.

Acabo de probar la creación de .7z, .rar, .zip, y .tararchivos tanto WinXP y Debian 5, y el .7zy .rarlos archivos / restaurar almacén de nombres de archivo correctamente mientras que el .zipy .tarlos archivos no. No importa qué sistema se use para crear el archivo de prueba.

quijote curandero
fuente
5

Tuve problemas con el desempaquetado tary los ziparchivos que recibo de los usuarios de Windows. Si bien no contesto la pregunta "cómo crear el archivo que funcionará", los siguientes scripts ayudan a descomprimir tary ziparchivar correctamente independientemente del sistema operativo original.

ADVERTENCIA: uno tiene que sintonizar la fuente que codifica manualmente ( cp1251, cp866en los ejemplos a continuación). Las opciones de línea de comandos pueden ser una buena solución en el futuro.

Alquitrán:

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

Cremallera:

#!/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 : uso el chardetpaquete para adivinar la codificación correcta de la porción de datos sin procesar. Ahora el script funciona de forma predeterminada en todos mis archivos malos, así como en los buenos.

Cosas a tener en cuenta:

  1. Todos los nombres de archivo se extraen y se fusionan en una sola cadena para formar una parte más grande del texto para el motor de adivinación de codificación. Significa que pocos nombres de archivo atornillados de una manera diferente pueden estropear la suposición.
  2. Se utilizó una ruta rápida especial para manejar un buen texto unicode ( chardetno funciona con un objeto unicode normal).
  3. Las pruebas de documentos se agregan para probar y demostrar que el normalizador reconoce cualquier codificación en cadenas razonablemente cortas.

Versión 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
fuente
¡Gracias por tus programas! Lamentablemente, el programa Zip no funciona en Python 3, pero funciona en Python 2.
Beroal
@beroal, actualicé el script. Ahora utiliza el motor desarrollado por Mozilla para Firefox para detectar automáticamente la codificación.
dmitry_romanov
4

POSIX-1.2001 especificó cómo TAR usa UTF-8.

A partir de 2007, la versión 6.3.0 del registro de cambios en PKZIP APPNOTE.TXT ( http://www.pkware.com/documents/casestudies/APPNOTE.TXT ) especificó cómo ZIP usa UTF-8.

Es solo qué herramientas soportan estos estándares adecuadamente, eso sigue siendo una pregunta abierta.

Damjan
fuente