Tuve problemas con el desempaquetado tar
y los zip
archivos 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 tar
y zip
archivar correctamente independientemente del sistema operativo original.
ADVERTENCIA: uno tiene que sintonizar la fuente que codifica manualmente ( cp1251
, cp866
en 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 chardet
paquete 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:
- 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.
- Se utilizó una ruta rápida especial para manejar un buen texto unicode (
chardet
no funciona con un objeto unicode normal).
- 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."