Oct 19, 2011
Perl で unzip.pl
2011年11月12日追記:ぽち*ぷ〜ちのいさましいちびのツールたち unzip.pl untar.pl enc.pl に unzip.pl についてもう少しだけ詳しく書きました。
UNIX/Linux で zipファイルの展開に使われる unzip という有名な展開ツールがありますが、ファイルシステムのロケールに設定された文字コードが異なる環境で作成された zipファイルで、日本語ファイル名が含まれているものを展開すると、ファイル名が文字化けしてしまいます。C で書かれた unzip のソースコードに、マルチバイト文字のファイル名に対応するパッチを当てれば文字化けしないようになるのですけども、Perl大好きなぽちはずっと以前から、狐の王国の Suganoさんが書かれた unzip.pl をもとに、ぽちが使い易いようにちまちま改造を加えたものを zipファイルの展開に使っています。最近のぽちの unzip.pl はこんな感じです。
unzip.pl
#!/usr/bin/env perl
use strict;
use warnings;
use Archive::Zip qw/ :ERROR_CODES /;
use utf8;
use Encode;
use Encode::UTF8Mac; # utf-8-mac は fオプションでしか使えません。
use Encode::Guess qw/ euc-jp cp932 iso-2022-jp utf8 /; # 自動推測の対象
use Encode::Locale;
use Path::Class;
use Getopt::Std;
my %opts;
getopts('f:', \%opts);
@ARGV or die "Usage: unzip.pl [-f encoding_name] zip_file\n";
foreach ( @ARGV ) {
my $file = file $_;
my $zip = Archive::Zip->new();
$zip->read( "$file" ) == AZ_OK or die "Couldn't read: $file\n";
my $str;
$str .= $_->fileName foreach ( $zip->members );
my $enc_name;
if ( $opts{ f } ) {
$enc_name = $opts{ f };
} else {
my $enc = Encode::Guess->guess( $str );
ref $enc ? $enc_name = $enc->name : die "Couldn't guess: $enc\n";
}
foreach ( $zip->members ) {
my $file = file( encode( 'locale_fs', decode( $enc_name, $_->fileName ) ) );
my $dir = $file->parent;
-d $dir or dir( $dir )->mkpath;
print "extract $file ...";
$zip->extractMember( $_->fileName, "$file" );
print "done.\n";
}
}
exit;
使い方は、
$ ./unzip.pl [-f エンコード名] zipファイル
基本的に fオプションは必要なく、zipファイルの中のファイル名からエンコードされている文字コードを推測して、そのファイル名をファイルシステムのロケールに合わせて変換してから展開してくれます。たまに推測に失敗する時もありますが、そんな時はエラーメッセージを見て fオプションで適切な文字コードを指定してあげます。
今 Archive::Zip の POD を読んでみたら、「展開するだけなら Archive::Zip じゃなくて Archive::Extract を使ってね?」なんて書いてありますね。けど Archive::Extractって一度展開した後でないと内容物のファイル名を取得出来ないんで、文字コードの変換をするにはちょっと不便なんですよねえ。
writeback message: