PHPで漢数字混じりのテキストを半角数字に統一する

(1/1)
ネットの新聞記事を利用する際、漢数字が混じっている場合があり、これを横書きのテキストにすると読みにくい。そこでPHPを使い、漢数字混じりの日本語テキストを半角数字に統一するプログラムをつくってみることにする。
「八戸市」「千灯明」といった名詞に含まれる漢数字を半角数字に変換しないようにするため、「PHPで形態素解析を行う」で紹介した「日本語形態素解析Webサービス」を利用する。

(2022年1月30日)PHP8対応,リファラ・チェック改良,https対応

目次

サンプル・プログラムの実行例

漢数字混じりのテキストを半角数字に統一検索

サンプル・プログラム

解説:準備

  44: //Yahoo! JAPAN Webサービス アプリケーションID
  45: //取得方法 https://www.pahoo.org/e-soul/webtech/php06/php06-01-02.shtm#Yahoo
  46: define('APPLICATION_ID', '*******************************************');

Yahoo! ウェブ検索」を利用するためには、Yahoo! JAPAN Webサービス アプリケーションID を取得する必要がある。その入手方法は「Yahoo!JAPAN デベロッパーネットワーク - WebAPIの登録方法」を参照されたい。
取得したIDは定数 APPLICATION_ID に格納する。

サンプル・プログラムの流れ

PHPで漢数字混じりのテキストを半角数字に統一する

解説:変換処理

 441: /**
 442:  * 漢数字を半角数字に変換する
 443:  * @param   string $kanji 漢数字
 444:  * @param   int $mode 出力書式/1=3桁カンマ区切り,2=漢字混じり, それ以外=ベタ打ち
 445:  * @return  string 半角数字
 446: */
 447: function kan2num($kanji, $mode) {
 448:     //半角数字が混在していたら何もしない
 449:     if (preg_match('/[0-9]+/ui', $kanji> 0)   return $kanji;
 450: 
 451:     //全角=半角対応表
 452:     $kan_num = array(
 453:         '0' => 0, '〇' => 0,
 454:         '1' => 1, '一' => 1, '壱' => 1,
 455:         '2' => 2, '二' => 2, '弐' => 2,
 456:         '3' => 3, '三' => 3, '参' => 3,
 457:         '4' => 4, '四' => 4,
 458:         '5' => 5, '五' => 5,
 459:         '6' => 6, '六' => 6,
 460:         '7' => 7, '七' => 7,
 461:         '8' => 8, '八' => 8,
 462:         '9' => 9, '九' => 9
 463:     );
 464:     //位取り
 465:     $kan_deci_sub = array('十' => 10, '百' => 100, '千' => 1000);
 466:     $kan_deci = array('万' => 10000, '億' => 100000000, '兆' => 1000000000000, '京' => 10000000000000000);
 467: 
 468:     //右側から解釈していく
 469:     $ll = mb_strlen($kanji);
 470:     $a = '';
 471:     $deci = 1;
 472:     $deci_sub = 1;
 473:     $m = 0;
 474:     $n = 0;
 475:     for ($pos = $ll - 1$pos >0$pos--) {
 476:         $c = mb_substr($kanji, $pos, 1);
 477:         if (isset($kan_num[$c])) {
 478:             $a = $kan_num[$c. $a;
 479:         } else if (isset($kan_deci_sub[$c])) {
 480:             if ($a !'')   $m = $m + $a * $deci_sub;
 481:             else if ($deci_sub !1)    $m = $m + $deci_sub;
 482:             $a = '';
 483:             $deci_sub = $kan_deci_sub[$c];
 484:         } else if (isset($kan_deci[$c])) {
 485:             if ($a !'')   $m = $m + $a * $deci_sub;
 486:             else if ($deci_sub !1)    $m = $m + $deci_sub;
 487:             $n = $m * $deci + $n;
 488:             $m = 0;
 489:             $a = '';
 490:             $deci_sub = 1;
 491:             $deci = $kan_deci[$c];
 492:         }
 493:     }
 494: 
 495:     $ss = '';
 496:     if (preg_match("/^(0+)/", $a, $regs!FALSE)  $ss = $regs[1];
 497:     if ($a !'')   $m = $m + $a * $deci_sub;
 498:     else if ($deci_sub !1)    $m = $m + $deci_sub;
 499:     $n = $m * $deci + $n;
 500: 
 501:     //出力書式に変換
 502:     if ($ss == '') {
 503:         $dest = $n;
 504:         switch ($mode) {
 505:             case 1:
 506:                 $dest = number_format($n);
 507:                 break;
 508:             case 2:
 509:                 $dest = int2kanji($n);
 510:                 break;
 511:             default:
 512:         }
 513:      } else if ($n == 0) {
 514:         $dest = $ss;
 515:     } else {
 516:         $dest = $ss . $n;
 517:     }
 518: 
 519:     return $dest;
 520: }

 522: /**
 523:  * 漢数字混じりのテキストを半角数字混じりテキストに変換する
 524:  * @param   string $str 漢数字混じりテキスト
 525:  * @param   string $func 形態素解析に使う関数
 526:  * @return  strin 変換後テキスト
 527: */
 528: function convert_kan2num($str, $func) {
 529:     //数字パターン
 530:     $pat_kannum = '/^[01234567890123456789〇一二三四五六七八九十百千万億兆京]+$/ui';
 531: 
 532:     //中黒の小数点
 533:     $str = preg_replace_callback('/([0123456789〇一二三四五六七八九十百千万億兆京]+)・([01234567890123456789〇一二三四五六七八九十百千万億兆京]+)/iu',
 534:         function ($mat) {
 535:             return kan2num($mat[1], 3. '.' . kan2num($mat[2], 3);
 536:         }, $str);
 537: 
 538:     //形態素に分解:Yahoo!JAPAN 日本語形態素解析Webサービス
 539:     $items = array();
 540:     $func($str, $items);
 541: 
 542:     //結果を1単語ずつ取得
 543:     $dest = '';
 544:     $flag = FALSE;
 545:     $numstr = '';
 546:     $i = 0;
 547:     for ($i = 0$i < count($items); $i++) {
 548:         if ($flag == FALSE) {
 549:             if (preg_match($pat_kannum, $items[$i]['surface']) > 0) {
 550:                 $numstr = $items[$i]['surface'];
 551:                 $flag = TRUE;
 552:             } else {
 553:                 $dest .$items[$i]['surface'];
 554:             }
 555:         } else {
 556:             if (preg_match($pat_kannum, $items[$i]['surface']) > 0) {
 557:                 $numstr .$items[$i]['surface'];
 558:                 $flag = TRUE;
 559:             } else if ($items[$i]['pos'] == '名詞') {
 560:                 $dest .$numstr . $items[$i]['surface'];
 561:                 $numstr = '';
 562:                 $flag = FALSE;
 563:             } else {
 564:                 $dest .kan2num($numstr, 2. $items[$i]['surface'];
 565:                 $numstr = '';
 566:                 $flag = FALSE;
 567:             }
 568:         }
 569:     }
 570:     if ($flag == TRUE) {
 571:         $dest .kan2num($numstr, 2);
 572:     }
 573: 
 574:     return $dest;
 575: }

入力テキストを改行で1行ずつに切り出し、ユーザー関数 convert_kan2num に渡し、漢数字混じりのテキストを半角数字に統一していく。

まず、テキストを日本語形態素解析Webサービスに渡し、形態素に分解する。後述するように、形態素の品詞を取得することが重要であるためだ。このWebAPIについては「PHPで形態素解析を行う」で紹介しているので、そちらを参考にしてほしい。

分解された形態素を結合して変数 $dest に代入していく過程で、漢数字を半角数字に変換する。
まず、その形態素が漢数字なら、漢数字をバッファリングする変数 $numstr に代入し、$flag をTRUEにする。$flag は前の形態素が漢数字かどうかを保持することになる。
その形態素が漢数字でなければ、半角数字変換後のテキストを格納する変数 $dest に追加する。

前の形態素が漢数字でなく、その形態素が漢数字なら、変数 $numstr に代入し、$flag をTRUEにする。
前の形態素が漢数字でなく、その形態素が漢数字でなければ、そこで漢数字が終了したものとみなし、ユーザー関数 kan2num を使って半角数字に変換する。ユーザー関数 kan2num は、「PHPで半角数字を漢数字にする」で作ったユーザー関数だ。

ここで注意が必要なのが、漢数字に続く形態素の品詞である。
例文で「千本」の部分は、「千(名詞)」と「本(接尾語)」に分解される。これはそのまま kan2num を使って半角数字「1000本」に変換すればいい。
一方、「千灯明」の部分は、「千(名詞)」と「灯明(名詞)」に分解される。これをそのまま半角数字に変換すると、「1000灯明」となってしまう。
そこで、漢数字の後に名詞が続く場合は、2つの形態素を合わせて1つの名詞とみなし、変換を行わないようにした。

参考サイト

(この項おわり)
header