漢数字文字列のもう一歩実用的な数値変換 - その2

 前のブログ記事では、数字をあらわす漢数字と大字の文字列について、あつかうための前処理とパタン、また変換のさいに考慮しなければならない細則について書きました。
ここから実際に文字列から数値へ変換をおこなっていきます。



実装はJavaScriptで行いました。
これはWebExtensionsによるブラウザ拡張がJavaScriptだからです。

daisy WarekiConv 和暦を西暦へ自動変換するFireFoxアドオン

daisy WarekiConv 和暦を西暦へ自動変換するChromeアドオン


JavaScriptにない機能を実装していくことになります。
つまり、parseInt()の漢数字版のようなものを、自分で実装することになります。

つまり、こういうことになります。
- 文字列中にある漢数字による桁と数を、それぞれ数値と桁のべき指数に変換する。
- それを順に足し合わせるなどして、文字列全体が表す数を得る。
- なお、漢数字文字列の桁のありなしを判定して処理をわける。


## 桁ごとの桁文字、数字文字の変換
'百'、'千'といった桁の文字、'壱'、'九'といった数字の文字を、それぞれ桁や数に変換します。

漢数字による数字文字の変換は、とうぜんJavaScriptの言語機能にはないので実装しています。

なお今回のプロジェクトでは、
- 数字文字の変換は cKanSuuzi2Int()
- 桁文字の変換は cKanSuuziKeta2Int()
で行っています。


// 漢数字(桁漢字なし)
```
function cKanSuuzi2Int(cKanSuuzi)
{
    const res0 = kanSuuziNumChars.indexOf(cKanSuuzi);
    if(-1 !== res0){
        return res0;
    }
    const res1 = daiziNumChars.indexOf(cKanSuuzi);
    if(-1 !== res1){
        return res1;
    }
    const res2 = daiziNumOtherCharPairs[cKanSuuzi];
    if(undefined !== res2){
        return res2;
    }
    const res3 = kanSuuziOtherCharPairs[cKanSuuzi];
    if(undefined !== res3){
        return res3;
    }
    return -1;
}

```

番号に並べてindexOfで返ってきた値をそのまま使う、というのがC言語ネイティブの書いたC的コードという感じがします。
(意味としては違うが数字は合っている。)

桁の変換も同じです。
桁の場合は、10のべき乗ということで、べき指数を返させています。
たとえば’千’の場合は3を返させて、使う側は10の3乗をして1000を得る、といったやり方をしています。
(JavaScriptでは`10 ** 3`という記法でべき乗計算できる。)


## 漢数字文字列を実際に数値へ変換する
『桁の有無』はこれにより数値への変換ロジックを切り替える必要があり、重要です。
判定方法としては、これもまた単純に、桁の文字を変数にあつめておいて正規表現で検出です。
ひとつでも桁を表す文字が含まれていれば桁有り、そうでなければ桁なしです。

『漢数字』『大字』は同じ数字表記のなかで混交しない雰囲気なのですが、分けておかなくても困らないのでチェックはせず、そのまま混在で判定しています。


### 桁なし漢数字文字列の変換
単純に、頭から1文字ずつ数値変換しながら10をかけて桁上がりさせていくだけです。
途中にゼロが入っていたりしても、専用の分岐処理がいりません。

コードも必要ないけれど全文引用してしまうくらい短いです。
```
function convKanSuuziStr2Int(sKanSuuzi)
{
    let val = 0;
    for(let i = 0; i < sKanSuuzi.length; i++){
        const num = cKanSuuzi2Int(sKanSuuzi[i]);
        if(-1 === num){
            // 変換を打ち切って結果を返す
            return val;
        }
        val *= 10;
        val += num
    }
    return val;
}

```
変換の打ち切りのところは、アドオンの実装上で末尾に『年』の文字がついた文字列が渡されてくることがあるのをナアナアで処理しているものです。
今回の用途では不正な文字列が入ってくることはないのですが、汎用をにらむと漢数字以外が混ざっていたら不正と判定するように変えるべき箇所ではあります。

後ろから処理していく方法でもよく、桁が最初から自明という利点もあるのですが、今回は頭から処理で実装しました。
こういうところの判断になんというべきか、今回のコードはJavaScriptで書いていますが全体的にCライクな空気があります。


### 桁あり漢数字文字列の変換
桁ありの場合、文字列の中に『数桁数...』と並んでいることになります。
前述のとおり数が1のときに『桁数桁』から数が省略されて『桁桁』になる場合もあります。

今回の実装は以下のようにしました。
頭から処理して『数』であれば数値に変換して変数にしまう。
桁であればべき指数Nに変換してから、10のN乗で値に展開します。
(’千’の場合は桁であるNは3、10の3乗で1000が得られる)
変数から数をとりだしてかけ合わせれば、その桁の値ができあがるので、結果をしまう変数に足しこみます。
これを漢数字文字列の最後まで行い、すべての桁を足し合わせることで数値変換を実現しています。

ここで主要なトリックは、数の変数を用意しておきあらかじめ1を入れておくことです。
桁桁と続いたときに、省略された1がそのまま存在するので、専用の分岐処理がいりません。
数の変数は、取り出して使ったら1に戻しておきます。


### 細則の、'20'を表す文字などについて
"参廿"で3*20=60、"廿十"で20*10=200とかやられる心配はしなくてよさそうなので、そのまま加算しています。


----
## おわり

というわけで、以上のことを考慮し、複数の記法に対応しているという意味で、いくらか現実世界に対応できるであろう変換機能を書いた次第です。

>『しかし輝く宝石の完璧さは、私たちがMozillaを書いたときの目的ではなかった』
「ストラテジーレターIV: ブロートウェアと80/20の神話(ジョエル・スポルスキ / 翻訳: 青木靖)」より

というやつかなと思いながらコードを書いていました。まあこの程度の内容はまったく複雑とは言わないわけではありますが。

今回、とりあえず不要として不正な文字列の検出を適当にしています。
年号を書き間違えているときにできることは大してないので、とりあえずいいだろうとしたのですが。
そのうち機会があればきちんとするかもしれません。

0 件のコメント:

コメントを投稿

Linuxコマンドライン上でSVGベクタ画像をJPG等へラスタライズ変換する

 Linuxコマンドライン上でSVGベクタ画像をJPG等へラスタライズ変換することができるが、上手く変換されない場合がある。   以前作った魔法陣イラスト素材をイラスト素材ストックサイトへ登録しようと思い立ち、改めて素材用にラスタライズ変換をかけようとした。   ラスタライズ変換...