Gauche で zip を読む

ウェブからアーカイブをまとめてダウンロードしたりすることがある。 だが、アップローダに上がっているものなんかはファイル名が連番になっていたりするので、その場でリネームするなりフォルダを分けておくなりしないと後で収集がつかなくなるのもよくあることだ。 よくあることはなるべく自動化しよう。

実際には個別の状況にあわせてルールを記述する必要があるだろうけども、まずアーカイブの中身が読めないことには話にならない。 ここでは単に zip 形式のアーカイブの中身を読むコードを Gauche で書いてみた。

(use binary.pack)
(use gauche.record)

(define-record-type pk0304 #t #t
  (signature) (ext-version) (general-bit) (compression-method)
  (update-time) (crc) (compressed-size) (file-size)
  (filename-size) (ext-field-len) (filename) )

(define (read-packet)
  (let1 header (apply make-pk0304 (unpack "VvvvVVVVvv"))
    (if (= (pk0304-signature header) #x04034b50)
        (begin
          (pk0304-filename-set! header
            (car (unpack #`"a,(x->string (pk0304-filename-size header))")))
          (unpack-skip #`"a,(x->string (pk0304-ext-field-len header))")
          (unpack-skip #`"a,(x->string (pk0304-compressed-size header))")
          header)
        #f
    )))

(define (zip-info filename)
  (with-input-from-file filename
    (lambda()
      (rlet1 files '()
        (while (read-packet) => x (push! files x))))))

zip-info は zip アーカイブに詰め込まれている各ファイルのメタ情報を取得してレコード (のリスト) として返す手続きだ。 例えばファイル名だけあればよいならこんな風にするとファイル名のリストを得ることが出来る。

(map pk0304-filename (zip-info "hoge.zip"))

ただ、日本語 (とかマルチバイト文字) のファイル名が使われている場合を無視しているので、そのままだと化けることがある。 それと言うのも、 zip フォーマットの仕様上は UTF-8 を使うことになっているにもかかわらず、実際には SJIS (CP932) になっていることも多いようでどう処理すべきか決められなかったからだ。 必要であれば適当に変換を狭んで欲しい。

広く普及しているものは技術が枯れて安心できると思っていたが、こうして見ると泥臭い要素も多分に有って思いの他グダグダであることに気付く。

Document ID: 46af1e1d8f22f9ea3050de69127d5bf5