ふそうごむ。by扶桑ゴム産業

扶桑ゴム産業の最新情報とうぇぶますたあ「TKYK」の個人的日記。

漢数字をアラビア数字にスマートに変換する

Yu君から「漢数字とアラビア数字の相互変換」のお題をもらう。久々にPerlをいじくったなって感じ。調べたらモジュール(Lingua::JA::Numbers)が存在していて、変換自体は簡単にできる。しかし、文章中から漢数字を抽出することは比較的敷居が高い

最初正規表現で抽出したが、ソース中の日本語文字列は、use utf8しないと、バイト単位で扱おうとするので変になっていた。調べたら、既に前に同じようなことがあってメモっていた…。

use utf8;

とすると、記述したコードはUTF8フラグが付いたものとなる。よって、

 my $word;

 utf8::decode($word); #UTF8フラグをつけて、文字単位で処理

 {

  use utf8;

  $word =~ s/([一二三四五六七八九十百千万億兆京]{1,70})/kanji2number($1)/egg;

 }

のように、明示して必要な時に用いることができる。

参考

Perl 5.8.x Unicode関連

iRSSの日記 - UTF-8がからむ文字化け解決

この問題は解決したが、文章中の「一方」とかも「1方」になったりしていた。正規表現だけではこれは難しい。

そこで、さらに品詞分解をMeCabに行わせて、数の部分のみを変換対象とするようにした。

以下、スマートでもビューティフルでもないソース。

#! /usr/bin/perl

use strict;

use CGI;

use Unicode::Japanese;

use Text::MeCab;

use Lingua::JA::Numbers;

use List::MoreUtils qw( mesh );

my $q = new CGI;

my $unijp = Unicode::Japanese->new();

my $mecab = Text::MeCab->new();

# 送られてきたデータを処理する -----------------

my $word = $q->param("word");

my $type = $q->param("option");

my $parse = $q->param("parse");

my @FEATURE_NAMES = ( '品詞', '品詞細分類1', '品詞細分類2', '品詞細分類3',

           '活用形', '活用型', '原形', '読み', '発音' );

print $q->header(  '-Content-Type' => 'text/html',

     '-charset'   => 'UTF-8',

    );

$word = $unijp->set($word)->z2hNum->get;

if ($type eq 'ja2num') {

 

 if ($parse eq 'mecab') {

  my $number_flag = 0;

  my @words;

  my $numword;

  for ( my $node = $mecab->parse( $word ); $node; $node = $node->next ) {

   if (length $node->surface) {

    if ( proc( $node )->{'品詞細分類1'} eq '数') { #数だったら

     $number_flag = 1;

     $numword .= $node->surface; #数の文字列をしまい込み

    }

    else {

     if ($number_flag == 1) { #数の文字列が終わったので変換作業へ

      push (@words , kanji2number($numword));

      $number_flag = 0;

      $numword = '';

     }

     push ( @words , $node->surface );

    }

   }

  }

  $word = join ('', @words );

 }

 else { #正規表現

  utf8::decode($word);

  use utf8;

  $word =~ s/([一二三四五六七八九十百千万億兆京]{1,70})/kanji2number($1)/egg;

 }

}

else {

 $word =~ s/([0-9,]{1,9})/number($1)/egg;

}

print "結果は以下の通りです。
";

print $q->textarea(

 -name => 'after',

 -rows => 10,

 -cols => 70,

 -default => $word,

);

sub number {

 my $num = shift || '0';

 $num =~ s/,//g; #カンマを削除

 $num *= 1; #いらない小数点を消す

 $num = num2ja($num, {style => $type});

 utf8::encode($num);

 return $num;

}

sub kanji2number {

 my $kanji = shift;

 print "(".$kanji.")";

 utf8::decode($kanji);

 my $num = ja2num($kanji, {style => 'kanji'});

 my ($i, $j);

 if ($num =~ /^[-+]?¥d¥d¥d¥d+/g) {

  for ($i = pos($num) - 3, $j = $num =~ /^[-+]/; $i > $j; $i -= 3) {

   substr($num, $i, 0) = ',';

  }

 }

 return $num;

}

sub proc {

 my ($node) = @_;

 my @features = split /,/, $node->feature;

 return { mesh( @FEATURE_NAMES, @features ) };

}

あとは、HTMLも適当に

<HTML>

<HEAD>

<META HTTP-EQUIV="Content-Type" CONTENT="text/html;CHARSET=UTF-8">

<TITLE>漢数字←→アラビア数字とかに変換するスクリプト</TITLE>

</HEAD>

<BODY>

<h1>漢数字←→アラビア数字とかに変換するスクリプト</h1>

<FORM ACTION="Numbers-convert.cgi" METHOD="POST"

ENCTYPE="application/x-www-form-urlencoded" name="form">

<h3>元のデータ</h3><br>

(全角数字は半角に変換します。)<br>

<textarea NAME="word" rows="10" cols="70"></textarea><BR>

<h3>変換文字:</h3><br>

<input type="radio" name="option" value="ja2num" id="op0" checked>

<label for="op0">漢数字→アラビア数字</label><br>

 <input type="radio" name="parse" value="mecab" id="mecab" checked>

<label for="mecab">MeCab【品詞分解】</label>

<input type="radio" name="parse" value="re" id="re">

<label for="re">正規表現</label><br>

<input type="radio" name="option" value="kanji" id="op1">

<label for="op1">アラビア数字→漢数字</label><br>

<input type="radio" name="option" value="romaji" id="op2">

<label for="op2">アラビア数字→ローマ字</label><br>

<input type="radio" name="option" value="hiragana" id="op3">

<label for="op3">アラビア数字→ひらがな</label><br>

<input type="radio" name="option" value="katakana" id="op4">

<label for="op4">アラビア数字→カタカナ</label><br>

<br>

<INPUT TYPE="SUBMIT" NAME="Submit" VALUE="変換!"><BR>

</FORM>

</BODY>

</HTML>

とかして、変換してみた。

変換前:

【ワシントン13日共同】米政府と連邦準備制度理事会FRB)は十三日夕、サブプライム住宅ローン問題の影響で業績が悪化している政府系住宅金融二社、連邦住宅抵当金庫(ファニーメイ)と連邦住宅貸付抵当公社(フレディマック)に対する緊急支援声明を発表した。両社の経営問題が世界の金融市場の重大関心事となっているため、危機回避へ日曜日の異例の支援表明になった。

 FRB声明によると、両社の短期的な資金繰りを支援するため、必要に応じてニューヨーク連邦準備銀行を通じて公定歩合で融資する。

 米メディアによると、フレディマックは資金調達のために三十億ドル(約三千二百億円)の債券発行を十四日に控えており、経営不安説が高まる中で支障なく資金が集められるかが焦点になっている。

 両社の経営問題が前週末の米欧株価の下落を呼び、週明けの東京市場への波及が懸念されていたため、市場の沈静化を目的に異例の対応になったとみられる。

 米紙ウォールストリート・ジャーナル(電子版)によると、両社が保有・保証している住宅ローン関連の証券化商品は約五兆二千億ドルと、米国の同証券化商品の半分近くを占める。

 一方、英紙サンデー・タイムズは十三日付で、市場における両社の信用を回復させるため、米政府が最大で百五十億ドルの資本注入を検討している、と伝えた。

 両社は、政府の住宅政策を担う重要な機関だが民間上場企業。報道内容が実現すれば、米政府が相当数の株式を保有することになる。

七面鳥

二面性

九死に一生スペシャ

五万二千ペソ

七転八倒

九章三節

山田一二三

で、変換後。

【ワシントン13日共同】米政府と連邦準備制度理事会FRB)は13日夕、サブプライム住宅ローン問題の影響で業績が悪化している政府系住宅金融2社、連邦住宅抵当金庫(ファニーメイ)と連邦住宅貸付抵当公社(フレディマック)に対する緊急支援声明を発表した。両社の経営問題が世界の金融市場の重大関心事となっているため、危機回避へ日曜日の異例の支援表明になった。

 FRB声明によると、両社の短期的な資金繰りを支援するため、必要に応じてニューヨーク連邦準備銀行を通じて公定歩合で融資する。

 米メディアによると、フレディマックは資金調達のために3,000,000,000ドル(約320,000,000,000円)の債券発行を14日に控えており、経営不安説が高まる中で支障なく資金が集められるかが焦点になっている。

 両社の経営問題が前週末の米欧株価の下落を呼び、週明けの東京市場への波及が懸念されていたため、市場の沈静化を目的に異例の対応になったとみられる。

 米紙ウォールストリート・ジャーナル(電子版)によると、両社が保有・保証している住宅ローン関連の証券化商品は約5,200,000,000,000ドルと、米国の同証券化商品の半分近くを占める。

 一方、英紙サンデー・タイムズは13日付で、市場における両社の信用を回復させるため、米政府が最大で15,000,000,000ドルの資本注入を検討している、と伝えた。

 両社は、政府の住宅政策を担う重要な機関だが民間上場企業。報道内容が実現すれば、米政府が相当数の株式を保有することになる。

七面鳥

二面性

九死に一生スペシャ

52,000ペソ

七転八倒

9章3節

山田一二三

詰めが甘いと思いますが、おおむねいい感じです。

この変換により、桁違いな話であることを実感できるツールになりそうです。