Perl で全角半角変換をモダンに行うコードを理解する
2009年06月07日
"Perl で半角カナと全角カナの変換をする" の記事を書いたら、"404 Blog Not Found:perl – で全角半角変換をモダンに行う" という CORE Module のみを使う方法というのが返ってきたのだけれど、Perl 特有の"呪文"というか"記号のお化け"のようなコードで何をしているのかがよくわからなかった…
そこで、ちょうど短いコードでもあったので1行ずつ何をしているのか調べていった。
"全角半角変換" の仕方としては、文字名(HALFWIDTH KATAKANA VOICED SOUND MARK など)から HALFWIDTH を削除して対応する全角カナ一覧を作り、tr/// で変換している。
eval の部分は NFC で合字の処理をするのに必要なのかな?hira2kata では NFC が必要ないから eval する必要もないのだろうか。
(追記:eval の部分について)
tr/// では変数展開が行われないために、$hankaku、$zenkaku を文字列内でそれぞれ変数展開してから eval するようになっている。
参考:perlop – Perl の演算子と優先順位
tr///で変数展開するにはevalする必要があるわけですが、 – 浅倉卓司@blog風味? – ひとりでもグループ
1 #!/usr/bin/perl 2 use 5.008001; 3 use strict; 4 use warnings; 5 use utf8; 6 use charnames ':full'; 7 use Unicode::Normalize; 8 9 { 10 my $hankaku = "\x{FF9E}\x{FF9F}"; 11 my $zenkaku = "\x{3099}\x{309A}"; 12 13 for my $o (0xFF61 .. 0xFF9D){ 14 $hankaku .= chr $o; 15 my $n = charnames::viacode($o); 16 $n =~ s/HALFWIDTH\s+//; 17 $zenkaku .= chr charnames::vianame($n); 18 } 19 20 *tr_h2z = eval "sub { local $_ = shift; tr/$hankaku/$zenkaku/; $_ }"; 21 *tr_z2h = eval "sub { local $_ = shift; tr/$zenkaku/$hankaku/; $_ }"; 22 23 sub han2zen { NFC(tr_h2z(shift)) } 24 sub zen2han { NFC(tr_z2h(NFD(shift))) } 25 26 sub hira2kata { 27 local $_ = shift; 28 tr/\x{3041}-\x{3096}/\x{30A1}-\x{30F6}/; 29 $_; 30 } 31 } 32 binmode STDOUT, ":utf8"; 33 local $\ = "\n"; 34 print zen2han(hira2kata("「ぽげむたぴぎゃみにょーん」って最初に言ったのは?")); 35 print han2zen("ウソダドンドコドーン");
- 2行目(
use 5.008001;) - Perl のバージョン番号を指定して、指定バージョンより新しいものであることをチェックする。
参考:perlfunc – Perl 組み込み関数 - 5行目(
use utf8;) - Perl にスクリプトが UTF-8 で書かれている事を教える。
参考:utf8 – ソースコード内に、UTF-8(か、UTF-EBCDIC)を有効/無効にするためのプラグマ - 6行目(
use charnames ':full';) - ダブルクォートされた文字列内で、名前でキャラクタを呼び出す(
\x{FF9E}といった部分)。
参考:Perl 5.8.x Unicode関連 - 7行目(
use Unicode::Normalize;) - Unicode 正規化 を使って合字(ここでは濁点・半濁点)を処理する。
参考:Unicode::Normalize で遊ぶ – daily dayflower - 10行目(
my $hankaku = "\x{FF9E}\x{FF9F}";) \x{FF9E}は半角の濁点(HALFWIDTH KATAKANA VOICED SOUND MARK) 、\x{FF9F}は半角の半濁点(HALFWIDTH KATAKANA SEMI-VOICED SOUND MARK)- 11行目(
my $zenkaku = "\x{3099}\x{309A}";) \x{3099}は全角の濁点(COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK)、\x{309A}は全角の半濁点(COMBINING KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK)- 13行目(
for my $o (0xFF61 .. 0xFF9D){) 0xFF61は半角の句点 "。"、0xFF9Dは半角カタカナの "ン"
"。" から "ン" までの文字に対して処理を行う。- 14行目(
$hankaku .= chr $o;) chr:引数で指定したコードに対応する文字を返す。
半角の濁点・半濁点からなる$hankakuに、半角の "句点" から半角カタカナの "ン" までを追加していく。- 15行目(
my $n = charnames::viacode($o);) charnames::viacode(code):コード番号 code の文字の名前を返す。(例:HALFWIDTH KATAKANA VOICED SOUND MARK)- 16行目(
$n =~ s/HALFWIDTH\s+//;) $nのHALFWIDTHを削除する。- 17行目(
$zenkaku .= chr charnames::vianame($n);) charnames::viacode(name):文字の名前 name のコード番号を返す。
全角の濁点・半濁点からなる$zenkakuに、返されたコード番号(HALFWIDTHを削除したもの)をchrで文字に変換した結果を追加していく。- 20行目(
*tr_h2z = eval "sub { local \$_ = shift; tr/$hankaku/$zenkaku/; \$_ }";) - 変数名のプレフィクスの
*は型グロブ。
参考:Perl講座 2章 [変数]
$_は入力レコード。tr/FROM/TO/は、検索文字列 FROM に含まれる各文字を対応する置換文字列 TO にマッチする文字に1文字ずつ変換する。
参考:tr/// [Perl講座 -Smart] - 23行目(
sub han2zen { NFC(tr_h2z(shift)) }) NFC:2つの文字を合字にする。
参考:Macの合字ファイル名で困ったときにはUnicode::Normalizeで処理すべし – 狐の王国
shiftで取り出した引数(変換対象文字列)をtr_h2z関数に渡す。- 24行目(
sub zen2han { NFC(tr_z2h(NFD(shift))) }) NFD:合字を2つの文字に分解する。
shiftで取り出した引数(変換対象文字列)をNFD関数に渡して2つの文字に分解するしてからtr_z2h関数に渡す。- 27行目(
local $_ = shift;) shift関数で@_の先頭を切り出しサブルーチンの引数を取得する。- 28行目(
tr/\x{3041}-\x{3096}/\x{30A1}-\x{30F6}/;) - ひらがな から カタカナ へ変換する。
"ぁ-ゖ" を "ァ-ヶ" に変換する。 - 29行目(
$_;) returnが無い場合は、最後に評価された値が返される。- 32行目(
binmode STDOUT, ":utf8";) use utf8;したので、文字列に UTF-8 フラグが付いているため PerlIO レイヤを使う。
参考:Perl 5.8.x Unicode関連- 33行目(
local $\ = "\n";) - 出力レコードセパレータに
\nを指定する。printの出力に自動的に\nが付加される。
ミニマルPerl Unix/LinuxユーザのためのPerl習得法
posted with amazlet at 09.06.05
Tim Maher
オライリージャパン
売り上げランキング: 279701
オライリージャパン
売り上げランキング: 279701
