PHP 超簡易 形態素解析の元のもと

形態素解析とは

文章を構成されている品詞(名詞、動詞、助動詞、副詞、形容詞、接続詞・・・)など最小単位に分離して解析すること。
AIの会話解析や、自動翻訳とか、幅広い分野で応用されています。
私的用途でも、大手検索会社がAPIで提供したりしていて最近は、かなり制度の良い解析が行われるようになりました。

今回は、PHPで1関数で終結してしまう超簡易版を作ります。

超簡易形態素解析をPHPで行う

超簡易形態素解析の内容

超簡易版なので、もちろん辞書を駆使したりとかは、まったくしません。
ただひたすら、文章を半角英数字、全角英数字、カタカナ、かな、漢字に分離して配列に放り込むだけです。

超簡易形態素解析のPHPソース

mb_eregを使用して正規表現で分離します。
テスト用に例文もソースの中に入っています。

<?php
header('Content-Type: text/html; charset=UTF-8');
mb_language("Japanese");
mb_internal_encoding("UTF-8");
mb_http_input("UTF-8");
mb_http_output('UTF-8');

// 語句分離テスト
// 例文
$str = "2018年5月ゴールデンウィークは、何処へ行きますか。現在の気温は、26.5度Tokyo海抜-1.7メートル";
echo "例文=$str<br><br>\n";
// 語句分離処理
$res = goku_sep($str);
// 結果
print_r($res);



//========================================================================
// 超簡易形態素解析(語句分離処理)
//
//
function goku_sep($str)
{
  $res = [];
  $temp_str = $str;
  while(1){
    $pos = -1;
    $chr_n = 0;
    if($n = mb_ereg("[一-龠]+", $temp_str, $match_array)){
      // 漢字
      $pos = strpos($temp_str, $match_array[0]);
      $match = $match_array[0];
      $chr_n = $n;
    }
    if($n = mb_ereg("[ぁ-ん]+", $temp_str, $match_array)){
      // かな
      $p = strpos($temp_str, $match_array[0]);
      if($pos > $p || $pos < 0){
        $match = $match_array[0];
        $pos = $p; $chr_n = $n;
      }
    }
    if($n = mb_ereg("[ァ-ヴー]+", $temp_str, $match_array)){
      // カタカナ
      $p = strpos($temp_str, $match_array[0]);
      if($pos > $p || $pos < 0){
        $match = $match_array[0];
        $pos = $p;
        $chr_n = $n;
      }
    }
    if($n = mb_ereg("[a-zA-Z0-9.-]+", $temp_str, $match_array)){
      // 半角英数字
      $p = strpos($temp_str, $match_array[0]);
      if($pos > $p || $pos < 0){
        $match = $match_array[0];
        $pos = $p; $chr_n = $n;
      }
    }
    if($n = mb_ereg("[a-zA-Z0-9]+", $temp_str, $match_array)){
      // 全角英数字
      $p = strpos($temp_str, $match_array[0]);
      if($pos > $p || $pos < 0){
        $match = $match_array[0];
        $pos = $p;
        $chr_n = $n;
      }
    }
    if($chr_n == 0){
      // なし
      break;
    }
    $res[]    = $match;
    $temp_str = substr($temp_str, $pos + $chr_n);
  }

  return $res;
}
?>

超簡易形態素解析のPHPソース実行結果

上のソースをそのままブラウザで実行すると。

こんな感じ、分かり易くソース変換すると。

例文=2018年5月ゴールデンウィークは、何処へ行きますか。現在の気温は、26.5度Tokyo海抜-1.7メートル<br><br>
Array
(
    [0] => 2018
    [1] => 年
    [2] => 5
    [3] => 月
    [4] => ゴールデンウィーク
    [5] => は
    [6] => 何処
    [7] => へ
    [8] => 行
    [9] => きますか
    [10] => 現在
    [11] => の
    [12] => 気温
    [13] => は
    [14] => 26.5
    [15] => 度
    [16] => Tokyo
    [17] => 海抜
    [18] => -1.7
    [19] => メートル
)

分離し過ぎとか言う意見もありますが、一応成功ではないでしょうか。

超簡易形態素解析のPHPソース補足

mb_eregの部分を一まとめにすれば、ソースは簡略化できます。


$n = mb_ereg("[一-龠]+|[ぁ-ん]+|[ァ-ヴー]+|[a-zA-Z0-9]+|[a-zA-Z0-9]+", $temp_str, $match_array);

それをあえてしない理由は2つ。

mb_eregを1行にしない理由、その1

PHP 5.2.0 からの
pcre.recursion_limit
pcre.backtrack_limit
への制限で与える文章によっては、処理不能となる恐れがある・・かもしれない!

mb_eregを1行にしない理由、その2

各文字形態で処理を分けた方が、今後のソースの展開に優位。
応用がきく。
などの理由です。