Saya punya masalah dengan membongkar tar
dan zip
file yang saya terima dari pengguna Windows. Meskipun saya tidak menjawab pertanyaan "bagaimana cara membuat arsip yang akan berfungsi", skrip di bawah ini membantu membongkar tar
dan zip
file dengan benar terlepas dari OS aslinya.
PERINGATAN: kita harus menyetel sumber encoding secara manual ( cp1251
, cp866
dalam contoh di bawah). Opsi baris perintah mungkin merupakan solusi yang baik di masa depan.
Ter:
#!/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:
#!/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 : Saya menggunakan chardet
paket untuk menebak pengkodean yang benar dari data mentah. Sekarang skrip bekerja di luar kotak pada semua arsip buruk saya, serta yang baik.
Hal yang perlu diperhatikan:
- Semua nama file diekstraksi dan digabungkan ke dalam string tunggal untuk membuat bagian teks yang lebih besar untuk mesin menebak enkode. Ini berarti bahwa beberapa nama file yang dikacaukan dengan cara yang berbeda masing-masing dapat merusak dugaan.
- Jalur cepat khusus digunakan untuk menangani teks unicode yang baik (
chardet
tidak berfungsi dengan objek unicode normal).
- Dokumen ditambahkan untuk menguji dan menunjukkan bahwa normalizer mengenali penyandian pada string yang cukup pendek.
Versi akhir:
#!/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."