У мене виникли проблеми з розпакуванням tar
та zip
файлами, які я отримую від користувачів Windows. Хоча я не відповідаю на запитання "як створити архів, який буде працювати", сценарії нижче допомагають розпакувати tar
та zip
файли правильно незалежно від оригінальної ОС.
УВАГА: необхідно налаштувати джерело кодують вручну ( cp1251
, cp866
в прикладах нижче). Параметри командного рядка можуть бути хорошим рішенням у майбутньому.
Дьоготь:
#!/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()
Поштовий індекс:
#!/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 : Я використовую chardet
пакунок, щоб відгадати правильне кодування необробленої частини даних. Зараз сценарій працює з усіх вікон у всіх моїх поганих архівах, а також у хороших.
Що слід зазначити:
- Усі імена файлів витягуються та об'єднуються в єдиний рядок, щоб зробити більший фрагмент тексту для механізму здогадки кодування. Це означає, що кілька прізвищ, прикручених по-іншому, можуть зіпсувати здогад.
- Спеціальний швидкий шлях був використаний для обробки хорошого тексту Unicode (
chardet
не працює з нормальним об'єктом unicode).
- Документи додаються для тестування та демонстрації того, що нормалізатор розпізнає будь-яке кодування на досить коротких рядках.
Фінальна версія:
#!/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."