PerlでUTF-8を扱う

Perlは以前は便利に使っていたが、徐々に使わなくなっていった。
書籍『はじめてのPerl - Perl4対応版』で覚えてLinux管理に大活躍してはいたのだが、

  • 複雑なデータ構造を扱うのがたいへん(配列の各要素がhashのkeyになってるようなデータをもたせようとすると、referenceをdereferenceするのが複雑すぎた)
  • UTF-8の扱いが理解できない

という問題があったため、最近は距離を置いていた。


だが、one-linerを書く言語としては未だPerlが一番よいように見えた。Node.jsはSTDINの扱いが面倒すぎるとわかった。Pythonはone-liner向きではないし、Rubyは ' end; end; end;'が読みにくそうだ。
perlUnix環境やCygwinで最初からインストール済みなことが多いのもいい。


そこでPerlUTF-8扱う方法をしっかり身につけることにした。


Web上の資料を読む

PerlUTF-8を扱う部分は dankogai が開発したらしい。なので、彼のブログを読むのがよいはずだ。


読んでわかったこと:

PerlUTF-8を扱うサンプルソース


STDINから読み込んだUTF8データを、そのまま出力するのは、これでよい。

#!/usr/bin/perl
use utf8;
use Encode;

my $content = '';
while(<>){
    $line = decode_utf8($_);
    $content .= $line;
}
print encode_utf8($content);


出力をUTF-8ではなくShift_JISにするなら

print encode_utf8($content);

の代わりに

print encode('cp932', $content);

と書く。


しかしこれでは、$contentに出力文字をため込んで、一気に吐き出す方法である。

その都度printするごとに print encode_utf8() と書くのも面倒だ。


都度printするなら、以下のようにするほうが簡単。

use utf8;
use Encode;
use open IN => ':encoding(cp932)'; #input:  ShiftJIS->Internal
use open OUT => ':utf8';           #output: Internal->UTF-8
use open ':std';                   # STDIN, STDOUT, STDERR

while(<>){
    print;
}

IN,OUTともに UTF-8文字列なら、

use open IN => ':encoding(cp932)';
use open OUT => ':utf8';

の代わりに

use open ':utf8';

でよい。


PerlでOne-Liner


しかしワンライナー

$ perl -pe "tr/ぁ-ん/ァ-ン/;" < ruby2.txt

と書きたいだけなのに、UTF-8のためだけに

$ perl -pe "use utf8; use open ':utf8'; use open ':std'; tr/ぁ-ん/ァ-ン/;" < ruby2.txt

と書きたくはない。以下の例はperl 5.10で実行した。
f:id:itouhiro:20130224212901p:plain


PerlUTF-8時代にはone-linerに、あまり向いてないとわかった。



PerlUTF-8の扱いはこうなってほしかった。

1. perl -u とするか、ソースコードに use utf8; と書けば、「文字列操作関数(length() とか正規表現とか)」がUTF-8対応版の動作をする。書かなければ 文字列操作関数は、文字列をバイト列として扱う。


2. 文字列はバイト列のまま。リテラル(ソースコード内に書かれた文字列)は、ソースコードがShiftJISで書かれてればShiftJISバイト列。ソースコードUTF-8ならUTF-8バイト列。STDINは、shellがUTF-8環境ならUTF-8バイト列としてが入ってくる。shellがCP932なら(gnupackのCygwinはコレ)、CP932バイト列として入ってくる。つまり文字列には何もしない。


3. ShiftJIS文字列はそのままでは文字列操作関数で正しく扱えない。なので、文字列操作関数に食わせる前に各自で、 $utf8txt = $shiftjistxt.enc('shiftjis', 'utf8'); とかでUTF-8バイト列に変換する必要がある。Shift_JIS出力したいなら、出力前にShiftJISバイト列に変換してから出力すればいい。


これだけなら、簡単に覚えられて、簡単にPerlUTF-8を扱えたのではと思う。One-Linerだって -u つければいいだけなんだからラクだった。


というか、PHPはそんな感じになっている。preg_replace()関数に u オプションをつければUTF8対応の動作になる等。


Rubyでone-liner

rubyだと -Ku オプションをつければ、one-linerでもUTF-8扱えるとわかった。以下の例(ruby 1.9.2)ではなぜか -Ku つけなくてもうまく動作している。
f:id:itouhiro:20130224220419p:plain

これはRubyを学び直す気が高まる。