Ich hatte Probleme beim Entpacken tar
und zip
Dateien, die ich von Windows-Benutzern erhalten habe. Während ich die Frage "Wie erstellt man ein Archiv, das funktionieren wird" nicht beantwortet wird, helfen die nachstehenden Skripts beim Entpacken tar
und der zip
korrekten Dateien, unabhängig vom ursprünglichen Betriebssystem.
ACHTUNG: man muss manuell die Quellencodierung abzustimmen ( cp1251
, die cp866
in den Beispielen unten). Befehlszeilenoptionen können in Zukunft eine gute Lösung sein.
Teer:
#!/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()
Postleitzahl:
#!/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 : Ich verwende chardet
Paket, um die korrekte Kodierung des rohen Datenblocks zu erraten. Jetzt funktioniert das Skript in allen meinen schlechten Archiven sofort und in einem guten.
Dinge zu beachten:
- Alle Dateinamen werden extrahiert und in die einzelne Zeichenfolge eingefügt, um einen größeren Textteil für die Codierungs-Schätz-Engine zu erstellen. Dies bedeutet, dass nur wenige Dateinamen, die auf unterschiedliche Weise verschraubt werden, die Vermutung stören können.
- Ein spezieller Fast-Path wurde verwendet, um einen guten Unicode-Text zu verarbeiten (
chardet
funktioniert nicht mit einem normalen Unicode-Objekt). - Doctests werden hinzugefügt, um zu testen und zu demonstrieren, dass der Normalisierer jede Codierung auf einer relativ kurzen Zeichenfolge erkennt.
Endgültige Version:
#!/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."