漢数字電卓
某所で必要だったので,漢数字電卓用のモジュールを作っていました.漢数字をアラビア数字に変換したり,アラビア数字を漢数字に変換するものです.そろそろできあがったかな,と思っていたら弾さんがLingua-JA-Numbers-0.05 - Converts numeric values into their Japanese string equivalents and vice versa - metacpan.orgというモジュールを作られたので,さっそくお蔵入りになりました.(T^T) このまま捨ててしまうのももったいないので,ここにさらします.
漢数字の表現は,一 二 三 四 五 六 七 八 九 十 百 千 万 億 兆 京 垓 禾予 穣 溝 澗 正 載 極 恒河沙 阿僧祇 那由多 不可思議 無量大数を使っています.万から極までは万進(10^4),恒河沙から無量大数までは万万進(10^8)です.
package JANumbers; use base qw(Exporter); use encoding 'shiftjis'; use bignum; use strict; our @EXPORT = qw(arabic2chinese chinese2arabic); ## 構造定義 # my ($tag1, $tag2, $tag3, $digit, @num1, @num2, @num3, @num4, %table); $tag1 = q{(?:千|百|十|\b)}; $tag2 = q{(?:載|正|澗|溝|穣|禾予|垓|京|兆|億|万|\b)}; $tag3 = q{(?:無量大数|不可思議|那由多|阿僧祇|恒河沙|極|\b)}; $digit = q{(?:一|二|三|四|五|六|七|八|九)}; @num1 = qw(一 二 三 四 五 六 七 八 九); @num2 = qw(十 百 千); @num3 = qw(万 億 兆 京 垓 予禾 穣 溝 澗 正 載); # 禾予を逆にしている @num4 = qw(極 恒河沙 阿僧祇 那由多 不可思議 無量大数); unshift @num1, ''; unshift @num2, ''; unshift @num3, ''; %table = ( '一' => 1, '二' => 2, '三' => 3, '四' => 4, '五' => 5, '六' => 6, '七' => 7, '八' => 8, '九' => 9, '十' => 10, '百' => 10**2, '千' => 10**3, '万' => 10**4, '億' => 10**8, '兆' => 10**12, '京' => 10**16, '垓' => 10**20, '禾予' => 10**24, '穣' => 10**28, '溝' => 10**32, '澗' => 10**36, '正' => 10**40, '載' => 10**44, '極' => 10**48, '恒河沙' => 10**56, '阿僧祇' => 10**64, '那由多' => 10**72, '不可思議' => 10**80, '無量大数' => 10**88, ); ## _fourchar($) # 万進の数字処理 # 文字列 $str を渡すと数字を返す # sub _fourchar($) { my $str = shift; my $ret; while ($str =~ /($digit)?($tag1)/g){ my $block = $table{$1}; # 二千だったら 二 my $unit = $table{$2}; # 二千だったら 千 ## 桁表記があるのに漢数字がない # 1 x 10^n の時 # $block = 1 if (!$block && $unit); ## 漢数字があるのに桁表記がない # n x 1 の時 # $unit = 1 if ($block && !$unit); $ret += $block * $unit; } return $ret; } ## _eightchar($) # 万万進の数字処理 # 文字列 $str を渡すと数字を返す # sub _eightchar($) { my $str = shift; my $ret; while ($str =~ /([^$tag2]+)($tag2)/g){ my $block = _fourchar($1); my $unit = $table{$2} || 1; $ret += $block * $unit; } return $ret; } ## chinese2arabic($) # 漢数字をアラビア数字に変換 # 文字列 $str を渡すと数字を返す # sub chinese2arabic($) { my $str = shift; my $ret; while ($str =~ /([^$tag3]+)($tag3)/g){ my $block = _eightchar($1); my $unit = $table{$2} || 1; $ret += $block * $unit; } return $ret; } ## _expo($$) # 数字 $num と桁 $pos を渡すと文字列を返す # sub _expo($$) { my $num = shift; my $pos = shift; my $ret; return '' if ($num == 0); return '一' if (($num == 1) && ($pos == 0)); return $num2[$pos] if (($num == 1) && ($pos > 0)); return $num2[$pos] . $num1[$num]; } ## _fourdigit($) # 万進の漢数字処理 # 数字 $num を渡すと文字列を返す # sub _fourdigit($) { my $num = shift; my ($pos1, $ret); map{ my $pos2; $ret .= $num3[$pos1++]; s/(\d)/_expo($1, $pos2++)/eg; $ret .= $_; }(reverse($num) =~ /\d{1,4}/g); return $ret = reverse($ret); } ## _eightdigit($) # 万万進の漢数字処理 # 数字 $num を渡すと文字列を返す # sub _eightdigit($) { my $num = shift; my ($pos1, $ret); map{ $ret = _fourdigit(reverse($_)) . $num4[$pos1++] . $ret; }(reverse($num) =~ /\d{1,8}/g); return $ret; } ## arabic2chinese($) # アラビア数字を漢数字に変換 # 数字 $num を渡すと文字列を返す sub arabic2chinese($) { my $num = shift; my ($fourdigit, $eightdigit); $num =~ /(\d{0,48}?)$/; return _eightdigit($`) . _fourdigit($1); } 1;
解説がいるかな?と思いつつ,このブログを見てるひとはそんなにいないだろうから,放置.あ,WinXP + Activestate上で作っていたので,文字コードにshiftjisを指定しています.