Ho avuto problemi con il disimballaggio tar
e i zip
file che ricevo dagli utenti Windows. Mentre non rispondo alla domanda "come creare l'archivio che funzionerà", gli script seguenti aiutano a decomprimere tar
e zip
archiviare correttamente indipendentemente dal sistema operativo originale.
ATTENZIONE: è necessario ottimizzare manualmente la codifica sorgente ( cp1251
, cp866
negli esempi seguenti). Le opzioni della riga di comando potrebbero essere una buona soluzione in futuro.
Catrame:
#!/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()
Cerniera lampo:
#!/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 il chardet
pacchetto per indovinare la codifica corretta del blocco di dati non elaborato . Ora la sceneggiatura funziona immediatamente su tutti i miei archivi danneggiati, oltre a quelli buoni.
Cose da notare:
- Tutti i nomi di file vengono estratti e uniti nella singola stringa per creare un pezzo più grande di testo per il motore di indovinare la codifica. Significa che pochi nomi di file avvitati in modo diverso possono rovinare l'ipotesi.
- È stato utilizzato un percorso rapido speciale per gestire un buon testo Unicode (
chardet
non funziona con un normale oggetto Unicode).
- I doctest vengono aggiunti per testare e dimostrare che il normalizzatore riconosce qualsiasi codifica su stringhe ragionevolmente brevi.
Versione 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."