2013年11月20日水曜日

Perlで漢字のファイル名の引数 & 漢字のファイルのopenのしかた

Perlで、引数にファイル名が与えられた時にファイルをunicodeファイルとして開く方法と、引数のファイル名そのものが漢字(日本語)の場合のperlの書き方について。

Macなので、TerminalはUTF8で動いているものとします。

while(<>)の場合

陰で自動的にファイルがopenされるので、それに対してbinmode ":utf8"を指定しなければなりません。

これはuse open ":utf8";です。

これで全openは自動的にUTF-8を指定したことになります。従って、陰でopenされる時もUTF-8のファイル内容として読み書きしてくれます。

@ARGVの中のファイル名

漢字のファイル名が引数になっていた場合、print $ARGV[0];すると文字化けします。渡されたファイル名の文字列がバイナリー扱いになっていて、perlの内部表現(use utf8;)になっていないためです。そこでdecodeを使って内部表現へ変換してやります。

decode("utf8", $ARGV[0]);は、第2引数はUTF-8の文字列なので内部表現に変換しろという意味。encode()じゃないのか?と思ってしまいますが逆です。

opendir() / readdir()

opendirに渡すファイル名の文字列は生のままの$ARGV[0]でも、decode("utf8", $ARGV[0])でも正常にopendirできるようです。

readdirしたファイル名もバイナリー状態なのでそのままprintすると文字化けします。decode()してから使います。ただ上述のようにopendirやopenの引数として使うだけなら、生のままでも使えます。

use strict;
use warnings;
 
use Encode qw(decode);
#use Data::Dumper;
use utf8;
 
binmode STDIN,  ":utf8";;
binmode STDOUT, ":utf8";
binmode STDERR, ":utf8";
use open ":utf8";

#
# example 1
#
# 'use open ":utf8";'がないと<>で自動的にopenされる
# ファイルをreadした時の文字コードがUTF-8にならない
#

while(<>) {
  print;
}

#
# example 2
#
# @ARGVの中身の文字コードが定まってない(バイナリー扱い)ので
# decode()を使って、UTF-8だと教えてやる。
# するとprintや文字列マッチで正しく扱えるようになる。
#
# open()やopendir()はdecode()してない文字列でも正常に動くようだ
# ファイル名を単にopen()/opendir()にしか使わないなら
# decode()を通さなくても一応問題なく使える
#
# readdir()で返ってくるファイル名も
# バイナリーのままなのでdecode()必要
#
# ここのopen()も'use open ":utf8";'によって
# UTF-8をread/writeするようになっているので
# <$FH>で読んだ行が文字化けせずにprintされる
#

foreach my $file (@ARGV) {
  $file = decode("utf8", $file);
  print STDERR "***$file\n";
 
  if (-d $file) { # directoryならファイル一覧を出してみる
    opendir my $DH, $file or die "$file: $!\n";
    while(readdir($DH)) {
      print decode("utf8", $_);
      #print "$_\n";  # これだと漢字のファイル名が文字化けする
    }
    closedir $DH;
  } else { # ファイルなら中身を出力してみる
    open my $FH, "<", $file or die "$file: $!\n";
    while(<$FH>) {
      print;
    }
    close $FH;
  }
}

参考にしたサイト

0 件のコメント:

コメントを投稿