背景
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するように修正。