それマグで!

知識はカップより、マグでゆっくり頂きます。 takuya_1stのブログ

習慣に早くから配慮した者は、 おそらく人生の実りも大きい。

rubyでWindows(sjis)Zipファイルを文字化けさせずにunzipする。

Windowsで作ったZIPファイルに日本語ファイル名やフォルダを含めると、UTF-8環境では化けます。その逆にUTF-8環境で作ったZIP日本語ファイルはWindowsで化けます。

文字化けさせずに取り出す。

文字化けさせない方法は色々あるがRubyでぱぱっと書くのが一番楽。

find -name "*.zip" -print0 | xargs -0 -I_ ./unzip.rb _
unzip.rb

(2011-09-07 追加 以下コードの最新版をこのエントリの末尾に追加)

#!/usr/bin/env ruby
# zipruby が必須
require 'rubygems'
require 'zipruby'
require 'fileutils'
require 'kconv'
OUTPUT_DIR = "./out/"
FileUtils.mkdir_p(OUTPUT_DIR) unless File.exist?(OUTPUT_DIR)
ZIPFILE_NAME = ARGV.first
Zip::Archive.open(ZIPFILE_NAME){ |entries|
 entries.each{|entry|
     name = entry.name.toutf8.gsub("\\", "/")
     if entry.directory?
         FileUtils.mkdir_p(OUTPUT_DIR+name)
     else
         dirname = File.dirname(name)
         FileUtils.mkdir_p(OUTPUT_DIR+dirname) unless File.exist?(OUTPUT_DIR+dirname)
         open(OUTPUT_DIR+name, "wb") do|f|
             f << entry.read
         end
     end
 }
}

rubyの場合ziprubyを使う

zipruby と rubyzip と二つあるが、rubyzipはJava脳の人が書いたJAVAっぽいメソッド。こまります。作者もjavaのZIP設計を移植したと描いている。Rubyから使いづらい。なのでZipRubyを使います。

findコマンドの解説

find コマンドでzipファイル探してxargsで展開してunzip.rbに渡します。

find -name "*.zip" #  *.zip にマッチするファイルをカレントディレクトリ以下で探す
     -print0       # ファイルの区切りに空白を使わない(ファイルに空白が含まれる場合)

xargs

xargs -0      # find -print0 を受け取るときはこう書く
   -I_        # findから受け取ったファイル名は _ とする
 ./unzip.rb _ # _ にfindから受け取ったファイル名を代入してコマンド実行

find / xargs 便利ですね。

WindowsのSJISで書かれたZIPを取り出す。そのほかの方法

WindowsのSJISで書かれたZIPをLinuxから扱うには他にも色々方法がありました。

そのほかの方法

コマンドからだと次の方法で何とかなる。

export LANG=C
7z x -o../out test.zip 
convmv -r --notest -f cp932 -t utf-8 
export LANG=ja_JP.UTF8

ただし、これはディレクトリ階層が入っているとうまくいかない。ディレクトリ区切り文字"\"が原因で、誤動作している気がする。

そのほかの方法2

ubuntu-jaチームが作っているunzipを使う

unzip -O cp932 test.zip

これもうまくいかない。たぶんLANG環境変数が誤作動の原因だと思う。

python , ruby などから使う

結局日本語化けさせないためにはRubyかくのが一番楽だった。あれこれ調べて2時間。Ruby書いたのは20分。圧倒的すぎた。

2011-06-15追記

この逆でLinuxからWindows用に,Zip作る場合はこっち
rubyでWindows(sjis)用のZIPファイルを作る。 - ブックマクロ開発に

2011-09-07 追記

出力ファイルフォルダを入力ファイルと同じにした

./unzip.rb test.zip # ./test/に展開される
ls *.zip | xargs -I@ ./unzip.rb @
#!/usr/bin/env ruby
# zipruby が必須
require 'rubygems'
require 'zipruby'
require 'fileutils'
require 'kconv'
ZIPFILE_NAME = ARGV.first
OUTPUT_DIR = "./" + File.basename(ZIPFILE_NAME,File.extname(ZIPFILE_NAME)) + "/"
FileUtils.mkdir_p(OUTPUT_DIR) unless File.exist?(OUTPUT_DIR)
Zip::Archive.open(ZIPFILE_NAME){ |entries|
 entries.each{|entry|
     name = entry.name.toutf8.gsub("\\", "/")
     if entry.directory?
         FileUtils.mkdir_p(OUTPUT_DIR+name)
     else
         dirname = File.dirname(name)
         FileUtils.mkdir_p(OUTPUT_DIR+dirname) unless File.exist?(OUTPUT_DIR+dirname)
         open(OUTPUT_DIR+name, "wb") do|f|
             f << entry.read
         end
     end
 }
}