ZipFileのsjis対策
zipの中身を見れるツールを作っていたところ、SJISで作られた(Windowsで作られた)アーカイブでは文字化けが発生することがわかった。
調べてみるとファイルパスにマルチバイト文字が使われているときにutf-8決めうちの処理があるのがまずくて、2箇所修正するところがある。
1個目は
http://chie.cc/?tag=python
に書かれているように、encode, deocdeの指定をutf-8からsjis(Windows決めうちでcp932にした)に変える。
2個目はlib/zipfile.pyの290行目くらいにある
if os.sep != "/" and os.sep in filename: filename = filename.replace(os.sep, "/")
いわゆるsjisのだめ文字問題でWindowsの場合にos.sepが"\(0x5c)"になるので、2バイト目に0x5C(バックスラッシュ/円マーク)がある文字が破壊される。
とりあえずコメントアウトすれば動く。
モンキーパッチ
import zipfile import sys class ZipInfo(zipfile.ZipInfo): def __init__(self, filename="NoName", date_time=(1980,1,1,0,0,0)): self.orig_filename = filename # Original file name in archive # Terminate the file name at the first null byte. Null bytes in file # names are used as tricks by viruses in archives. null_byte = filename.find(chr(0)) if null_byte >= 0: filename = filename[0:null_byte] # This is used to ensure paths in generated ZIP files always use # forward slashes as the directory separator, as required by the # ZIP format specification. # !!!! コメントアウト !!!! #if os.sep != "/" and os.sep in filename: # filename = filename.replace(os.sep, "/") self.filename = filename # Normalized file name self.date_time = date_time # year, month, day, hour, min, sec # Standard values: self.compress_type = zipfile.ZIP_STORED # Type of compression for the file self.comment = "" # Comment for each file self.extra = "" # ZIP extra data if sys.platform == 'win32': self.create_system = 0 # System which created ZIP archive else: # Assume everything else is unix-y self.create_system = 3 # System which created ZIP archive self.create_version = 20 # Version which created ZIP archive self.extract_version = 20 # Version needed to extract archive self.reserved = 0 # Must be zero self.flag_bits = 0 # ZIP flag bits self.volume = 0 # Volume number of file header self.internal_attr = 0 # Internal attributes self.external_attr = 0 # External file attributes # Other attributes are set by class ZipFile: # header_offset Byte offset to the file header # CRC CRC-32 of the uncompressed file # compress_size Size of the compressed file # file_size Size of the uncompressed file def _decodeFilename(self): if self.flag_bits & 0x800: return self.filename.decode('cp932') # 日本語Windows用 else: return self.filename zipfile.ZipInfo=ZipInfo