LoginSignup
6
7

More than 5 years have passed since last update.

Windowsで文字化けを起こさずにzip圧縮するpythonスクリプト for Mac

Last updated at Posted at 2015-03-27

背景

Mac -> Windows(日本語環境)でzipファイルを渡す時、Macデフォルトのzip圧縮ではファイル名の文字コードがutf-8のままになり、Windowsで解凍すると文字化けが起こります。
そうならないように MacWinZip のようなソフトをインストールして圧縮するようにするということもできますが、インストールは何かと面倒臭いもの。
そこで、Python 2.7 で動作するWindows向けZip圧縮スクリプトを作成しました。

Windows -> Mac の解凍

僕の使っている OSX Yosemite では Windowsで作成されたzipファイルを問題なく解凍できますが、linuxなどの環境ではsjisのファイル名が文字化けしてしまうことがあるらしいので、その場合、ファイル名をutf-8に変更して解凍するスクリプトを用いましょう(解凍スクリプトの例)。

使い方

$ python win_zip.py <圧縮したいフォルダのパス>

注意

  • 動作確認環境:Mac OSX Yosemite, Python2.7.6 (Python 2.4以降なら動作するはず)
  • 動作は一通り確認しましたが、コード実行の責任は負いません。
  • Macで作成される .DS_STOREなどの隠しファイルを一括で除外するため、 隠しファイルは全てzipフォルダに含まれません
  • このコードで作成したzipファイルはWindowsでは文字化けなしに解凍できますが、逆にMacだと文字化けします。上記の解凍スクリプトなどを使いましょう。
  • ファイルの内容は Utf-8のままです。sjisに変換するのはファイル名のみです。

コード(win_zip.py)


# coding:utf-8
import os, sys, zipfile, shutil
from unicodedata import normalize

def mac_path_to_sjis(mac_path):
    # macのファイル名は濁点が分割されているため、その問題を解消
    # これを行わなければ、濁点がsjisに変更できない例外が発生する
    norm_path = normalize('NFC', unicode(mac_path,'utf8'))
    return norm_path.encode('sjis')

def check_yes_no(message):
    # raw_input returns the empty string for "enter"
    yes = set(['yes','y'])
    no = set(['no','n'])
    choice = raw_input(message + os.linesep).lower()
    if choice in yes:
        return True
    elif choice in no:
        return False
    else:
        print "Please respond with 'yes' or 'no'"
        return check_yes_no(message) # loop

def zip_dir(in_dir_path):
    in_parent_path, in_dirname = os.path.split(in_dir_path)
    os.chdir(in_parent_path)
    with zipfile.ZipFile(in_dirname + '.zip', 'w') as zip_file:
        for parent_path, dirs, files in os.walk(in_dirname):
            sjis_dir_path = mac_path_to_sjis(parent_path)
            os.makedirs(sjis_dir_path)
            for fname in files:
                if fname[0] == '.': continue # ignore all secret file
                file_path = os.sep.join((parent_path, fname)) # path of original file
                print 'zipping : {0}'.format(file_path)
                sjis_file_path = mac_path_to_sjis(file_path)
                if file_path == sjis_file_path:
                    zip_file.write(file_path) # file name is all alphabet
                else:
                    shutil.copyfile(file_path, sjis_file_path) # copy file in sjis file name
                    zip_file.write(sjis_file_path)
                    os.remove(sjis_file_path) # remove sjis format file
            # remove parent folder in sjis format
            os.removedirs(sjis_dir_path)


if __name__ == '__main__':
    if len(sys.argv) != 2:
        sys.exit('Only 1 argument(path of directory) has to be specified as argument!')
    zip_target_path = sys.argv[1]
    if os.path.exists(zip_target_path) == False:
        sys.exit('Directory {0} does not exists!'.format(zip_target_path))
    if zip_target_path.endswith(os.sep):
        zip_target_path = zip_target_path[0:-1] # remove seperator
    zip_path = zip_target_path + '.zip'
    if os.path.exists(zip_path):
        if check_yes_no('Zip file already exists. Do you want to replace? [y/n]') == False:
            print 'Bye'
            sys.exit()
    zip_dir(zip_target_path)

何か問題や改善点などございましたら、遠慮なくご教示ください。

*20150628: zipしたフォルダの階層が /Users/username/ から始まるようになっていたので、必要なフォルダだけzipするように修正。

6
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
7