Windows上でperlを使って日本語ファイル名を操作する(メモ


slashdot.jpの「Perlはゾンビだ」に、日本語Windows環境において、perlを使って日本語ファイル名を操作する時の注意点がまとめられていたので、引用&備忘録化。

過去にperlスクリプトを作る際、ファイル内に書かれた日本語の操作に関することはWindows/Linuxで動作が共通化できたものの、「日本語ファイル名の取り扱い」について、Windows上での動作がいまいち分からず、面倒になり、NASに該当ファイル群を置き、Linuxで処理する、ということをしたことがある。

その時に受けた感じからすると、以下の解説は非常に納得がいくモノだった。

Anonymous Coward の書き込み (#2628619) 」より

コマンドラインからの引数取得とかファイル操作とかのたびに
@ARGV = map { decode(‘cp932’, $_) } @ARGV; とか
open my $fh, ”, encode(‘cp932’, $filename) or die $!; とか
@files = map { decode(‘cp932’, $_) } readdir($d); とか
if (-f encode(‘cp932’, $filename)) とか
mkdir encode(‘cp932’, $filename); とかソース全体にわたって書かなければならないこと。

これが面倒だと思わない奴はWindows上でPerlを本気で使ったことがないとしか思えない。少なくとも小飼弾はMac使っているようだし。つーか面倒だと思わないならそもそもどうして開祖が「怠惰はプログラマの美徳」とか言ってる言語なんか使ってるの?

56億7千万歩ほど譲ってこれは面倒ではないということにしても、Windows以外の環境への移植性がまったくない。Windows上でシステムロケールを変えただけで動作がおかしくなる。当然どんな環境で動くかわからないモジュール内では使えない。
まあいちおうこれには対処の方法があって、
use Encode::Locale;
@ARGV = map { decode(locale, $_) } @ARGV; とか
open my $fh, ”, encode(locale_fs, $filename) or die $!; とか
@files = map { decode(locale_fs, $_) } readdir($d); とか
if (-f encode(locale_fs, $filename)) とか
mkdir encode(locale_fs, $filename); とかさらに呪文を積み増しすればいいらしい。
MacやLinuxでこんなことわざわざしてる奴いないと思うけど。つーか忘れてもたいてい問題なく動くからWindows以外の環境で正しく書くのが超困難。だから結局Windowsに移植する奴が貧乏クジを引かされる。Windows上ですら、localeとlocal_fsが違う環境があるというバグを知らずに踏んでも気づかない。

しかし実はEncode::Locale;使ってもまだ不十分で、システムロケール(日本語版ならシフトJIS)で表せない文字を含んだファイル名が扱えない。いちおうWin32::Unicode::Fileというモジュールがあるようだが、ファイル読み書きをモジュール内で独自に行なっているので信じられないほど遅くて捨てた。Win32::Longnameというモジュールがちょっとはマシなようだが、どちらにしてもまたも移植性がなくなる。

自分で書くコードは上記の面倒じゃないことにした書き換えを「するだけで」済むとしても、File::compareのような超基本的モジュールすらいまだに非対応。

Perlでこのへんが改善される見込みはまったくなさそうなので、自分はPythonの勉強を始めた。

そして、(#2630401) より

> ・PerlをUTF-8ベース(CP65001)でPerlを動かす
> ことだと思います。

Console CPはファイル名で使われるコードページにはまったく影響を与えない。Encode::Localeでもlocale_fsとconsole_inとconsole_outが別々に定義されている(そう、APIレベルではコンソール入力と出力さえ異なる可能性がある)。Perlの仕様をまともにするためにあなたのような知ったかぶりバカをマサカリで殴って回るよりコストがかからないに違いないからPythonの勉強を始めたの。

> 普通のコマンドプロンプト(SJIS)ベースでは不可能。

Windowsにはコンソール入出力にもワイド文字APIがあり、それを使えば可能(リダイレクトやパイプが絡むとこっちの都合だけでエンコーディングを決められないけどそれはCygwinでも同じだし)。もちろん今あるPerlでは不可能だがそれは実装の手抜きにすぎない。そもそもCygwinはどうやってUTF-8文字を受け付けるコンソールを実現してると思ってるの?

> ・UTF-8 な cygwin の環境を構築し、その上で Perl を使う

Cygwinはパスの扱いが特殊すぎて、結局普通のアプリとパス名をやりとりする際にパスの変換/逆変換が必要なので、面倒だとか移植性ゼロだとかいう問題は何も解決しない。Perlがmsvcrt使うのやめて、UTF-8←→UTF-16LE変換を行ってワイド文字API呼び出して、Encode::LocaleはあたかもロケールがUTF-8であるかのようにふるまってくれるのが一番いいのだが、それを実現するために投入するコストを考えるとすでにまともな処理している言語の乗り換えにコストを振り向けたほうがどう考えても省エネ。

> ・@ARGVやファイル名を、スクリプト本体内のデータ処理と混ぜない

それを面倒と思わないなら以下略

  1. くるみぱんっ!

    >File::compareのような超基本的モジュールすらいまだに非対応。
    CPANにStable::Moduleがありますよ 😛

Leave a Comment


NOTE - You can use these HTML tags and attributes:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>