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