Eu tive problemas com a descompactação tar
e os zip
arquivos 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 tar
e zip
arquivos corretamente, independentemente do sistema operacional original.
ATENÇÃO: uma tem a sintonizar a codificação da fonte manualmente ( cp1251
, cp866
nos 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 chardet
pacote 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:
- 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.
- O atalho especial foi usado para manipular um bom texto unicode (
chardet
não funciona com um objeto unicode normal).
- 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."