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