試験運用中なLinux備忘録・旧記事

はてなダイアリーで公開していた2007年5月-2015年3月の記事を保存しています。

PythonでZIPファイルを展開する(メモ)

Pythonではzipfileというモジュールを用いることによりZIP書庫の読み書きが行える。
ここでは既存のZIP書庫の展開をPythonで行う処理を扱う。

ZIP書庫を扱うオブジェクト

zipfile.ZipFileオブジェクトを用いる。

  • 開くときにファイル名とモードを指定
  • 閉じるときにメンバ関数close()を使用
  • 読み書きが失敗すると例外IOErrorを出す

といった点においてファイルオブジェクトに似ている。

import zipfile
import sys
(中略)
infile = [ZIPファイルの場所]
try:
  f_zip = zipfile.ZipFile(infile, 'r')
except IOError:
  print >> sys.stderr, 'IOError'
  sys.exit(1)
try:  # finally
  try:
    [読み書き処理...]
  except IOError:
    print >> sys.stderr, 'IOError'
finally:
  f_zip.close()

ファイル/ディレクトリ名とタイムスタンプを処理する

zipfile.ZipFileオブジェクトのメンバ関数namelist()はZIPファイル中のファイルやディレクトリの一覧を格納するリスト(各要素は書庫内パスの文字列)を返し、infolist()はその一覧のファイル/ディレクトリの並びと同じ順番で個別の項目に関するタイムスタンプなどの情報を含むzipfile.ZipInfoオブジェクトのリストを返す。
これらをPythonの組み込み関数zip()で同時に処理すると便利。

for (item, info) in zip(f_zip.namelist(), f_zip.infolist()):
  [itemで個別のファイル/ディレクトリの書庫内パス、infoでその項目の情報を取り出す]

ディレクトリのタイムスタンプを復元するためには、全てのファイルを展開した後でディレクトリのタイムスタンプを変更することになる。あらかじめファイルとディレクトリを分けてから順番通りに処理する必要があるので面倒。

ファイルとディレクトリの展開

各書庫内パス(namelist()で得られるリストの個別の文字列)はファイルの場合とディレクトリの場合とがあるが、ディレクトリの場合は末尾が「/」になっているため、区別の役に立つ。
ディレクトリの場合はos.makedirs()などを用いて作成し、ファイルの場合は通常のファイルと同様open()で(バイナリの書き込みモードで)開いた後でzipfile.ZipFileオブジェクトのメンバ関数read()に書庫内パスを渡したものを書き込むことでZIPファイル内の個別のファイルの中身を取り出す。
ただし、Windows上の日本語ファイル名を含むZIPファイル*1を処理する場合、ZIPファイル内のファイル名エンコーディングはCP932(Windows上のShift_JIS)だが展開先のGNU/Linux上ではUTF-8となるため、作成するファイル/ディレクトリ名についてtry-except文によって各エンコーディングでのデコードを試行していく形となる。
下はディレクトリ作成部分。ファイルの作成部分も同様に処理することになるが、ここでは省略する。

prefix = [展開先ディレクトリ]
for (item, info) in [ディレクトリ一覧の(名前, 情報)のタプルのリスト]:
  try:
    item_unicode = item.decode('utf-8')  # UTF-8
  except UnicodeDecodeError:
    try:
      item_unicode = item.decode('cp932')  # Win上の日本語ディレクトリ名
    except UnicodeDecodeError:
      item_unicode = item.decode('ascii')
  outpath = os.path.join(prefix, item_unicode.encode('utf-8'))
  try:
    os.makedirs(outpath)
    print '[OK] makedirs: %s' % outpath
  except OSError:
    print >> sys.stderr, [エラーメッセージ...]

とりあえず上のようにしてみたものの、これで良いのかは分からない。

タイムスタンプの復元

タイムスタンプはzipfile.ZipInfoオブジェクトのメンバdate_timeとして取り出すことができるが、その形式が特殊で、6つの値となる。*2
タイムスタンプを変更するためのos.utime()を用いるためには

  1. date_timeの各値を一度文字列の形にフォーマット付け(文字列に変換)する
  2. time.strptime()でその文字列を解析
  3. time.mktime()で形式変換

とする。

timestamp = time.mktime(time.strptime('%d/%02d/%02d %02d:%02d:%02d' % info.date_time, '%Y/%m/%d %H:%M:%S'))

(「PythonでZIPファイルを展開する(コード例)」に続く)

関連記事:

参考URL:

使用したバージョン:

*1:Wine上のWindowsアプリケーションで作成したものも同様

*2:タプルに西暦年/月/日/時/分/秒がそれぞれ入っている