PHPとKAKASIを使って単語に分解する(その1)

(3/3)

サンプル・プログラムの解説:PHPから KAKASI を呼び出す

0028:     //形態素解析をしたい文章を渡しつつ、kakasiへのハンドルオープン
0029:     $handle = popen("echo '$str' | $kakasi -w ", "r");

PHP から KAKASI を呼び出し、本プログラムの中心機能を担っているのが parsing 関数である。
KAKASI には、コマンドライン・オプションを使って様々な機能を提供する。ここでは w オプションを使い、分かち書き(単語に分解)機能を利用させてもらう。
space
echo コマンドを使って入力データ(日本語テキスト)$str をパイプを使って KAKASI に渡す。コマンドラインで、この処理を実行すると、画面(標準出力)に分解された単語がスペース区切りで出力される。
関数  popen  は、実行したコマンドの出力結果を関数  popen  で取得できるようにするストリーム関数である。
なお、PHPセーフモードで動作しているようなサーバ(当サイトもそうである)では、関数  popen  の動作が許可されない。この場合は、本プログラムを CGI として動作させる必要がある。具体的には、ソース・ファイルの冒頭行に PHP の実行パス(例 #!/usr/bin/php)を記述し、parsewords.cgi という名前でセーブし、実行権限を与える。これでも動作しない場合は、サーバ管理者と相談してほしい。

0031:     //結果を1行ずつ取得
0032:     while ($get_kakasi = fgets($handle)) {
0033:         //kakasiの結果を分解
0034:         $result = split("[\t\r\n' ]", $get_kakasi);
0035:         //結果を配列に格納する
0036:         foreach ($result as $key=>$val) {
0037:             if ($val != '') {
0038:                 if (isset($array[$val]))        $array[$val]++;
0039:                 else                            $array[$val] = 1;
0040:             }
0041:         }
0042:     }

関数  popen  でオープンしたハンドルから処理結果を順次関数  popen  で取り出し(60行目)、区切り文字を指定して配列変数に分解する。
space
PHP では文字列を添え字とする連想配列が利用できる。配列 $array は単語を添え字とする連想配列とし、出現回数を変数値として登録していく。

サンプル・プログラムの解説:結果の表示

0074: //1行ずつ取り出して単語に分解する
0075: $str = strtok($contents, "\n");
0076: while ($str != FALSE) {
0077:     parsing($Kakasi$str$keywords);
0078:     $str = strtok("\n");
0079: }
0080: 
0081: arsort($keywords);       //出現回数の多い順に並び替え

関数 parsing を呼び出す。
ここでは入力テキストから1行ずつ文字列を取り出すために関数  strtok  を利用した。(この処理に関するアドバイスをいただいた jk さんに御礼申し上げます)
処理結果の出力は table 形式にしたが、for ループを使って出現回数の多い順に一覧表示するようにした。

質疑応答

【質問】 さだ様より
このページで公開されているスクリプトを DL させていただき、http://kakasi.namazu.org/ から、kakasi-2.3.4.zip を DL し、テスト環境ということもあり、parsewords.php と同じディレクトリ内に kakasi を設置いたしました。
#parsewords.php に記述のあるパス(テスト環境は WinXP)は通しました。
で、そこで該当ファイルを実行すると、結果として
■分解した単語
出現回数 単語
と、表示されるだけで、ポストしたデータは何も反映されません。
kakasi のインストールに関しても、正常な設定があるのだろうか?と色々と調べてはみたものの、知識に乏しい私には解決に至る内容がなく、失礼ながらにも、こちらに投稿した次第です。
そこで質問なのですが、kakasi のインストールは、単に設置するだけでは駄目なのでしょうか?もし駄目であれば、その対処法を教えてはいただけませんか?(C:\Autoexec.bat を書き換えるとの内容をあるサイトでも拝見していますが、そのファイル名がなかったりで、何をしたら良いのか分からない状態です)
また、テスト終了後、実際にレンタルサーバーへアップして利用したいと考えているのですが、その場合、kakasi の設定はどのようにしたら宜しいのでしょうか?
以上、突然の質問で恐縮ですが、アドバイスなど頂戴出来れば幸いです。お忙しい中恐縮ですが、宜しくお願い申し上げます。
【回答】
まずは、障害の切り分けを行いましょう。
以下の1~3の結果をお知らせください。

1.入力データの確認
71行目の直後に
  echo $contents;
  exit(1)
を挿入し、入力された文字が正しく渡されていることを確認してください。

2.kakasi 動作の確認
29行目の直後に
  echo $handle ? 'TRUE' : 'FALSE';
  exit(1)
を挿入し、FALSE になっていないことを確認してください。

3.結果取得状況の確認
77行目の直後に
  print_r($keywords);
  exit(1)
を挿入し、結果が配列に代入されていることを確認してください。
【質問】 さだ様より
只今アドバイスを頂戴した内容を確認いたしましたところ、

1.入力データの確認 → 正常にデータは渡されている。
2.kakasi 動作の確認 → TRUE になっている。

ですが、最後の結果取得状況の確認である配列には何もセットされていない状態でした。
お忙しい中、お疲れのところ大変恐縮ではありますが、引き続きご指導いただければ幸いです。
【回答】
kakasi をインストールしたディレクトリに空白が含まれていませんか?

ご承知のように、Windows で空白を含むディレクトリを指定する場合にはダブルクォーテーションで囲まなければならないのですが、これをやると popen関数が動作しなくなります。
kakasi は C ドライブ直下に kakasi ディレクトリをつくり、そこに配置することをお勧めします。

どうしても他ディレクトリに配置する場合は、空白のないディレクトリ名にすることと、環境変数 KANWADICTPATH と ITAIJIDICTPATH を作成し、それぞれの値に "インストール先\share\kakasi\kanwadict" と "インストール先\share\kakasi\itaijidict"を設定してください。
【質問】 さだ様より
アドバイスを頂戴したように、kakasi を C ドライブ直下に設置することで、ポストしたデータがきちんと配列にセットされるまでには至ったのですが、文字化けが出ている状況です。

アドバイスを頂戴し、こちらでも色々と調べてはいるのですが、windows に関する情報が乏しく(もちろん私の知識も。汗)、スクリプト側で mb_convert_encoding()にて対処しようと試みていますが、思うような結果を得られずにいる次第です。

ぱふぅ様のスクリプトを DL させていただき、現状ではそのままテストを行っていますが、他のスクリプトに取り入れたく、ぱふぅ様のスクリプトにある、
 mb_internal_encoding('SJIS');
と、
 <meta http-equiv="Content-Type" content="text/html; charset=Shift_JIS" />
を、EUC に変えている次第です。(元のままの文字コードでも文字化けは直らず)
【回答】
Windows の echo コマンドでは、シフト JIS 文字以外を扱うことはできません。
そこで、対策として2つの方法が考えられます。

1.一時的にシフト JIS に変換する
まず、ユーザー関数 parsing の中で関数 popen を使って kakasi に文字列を渡す直前に、
 $str = mb_convert_encoding($str, 'SJIS', 'EUC-JP');
として、シフト JIS に変換します。そして、popen から取得した値を分解する while ループの中で、関数 split で分解する直前で EUC-JP に戻してやります。
 $result = mb_convert_encoding($str, 'EUC-JP', 'SJIS');
なお、スクリプトの冒頭は
 mb_internal_encoding('EUC-JP');
に変更して下さい。

2.一時ファイルを用意する
echo コマンドで kakasi に文字列を渡す代わりに、EUC-JP 文字列を一時ファイルに保存し、kakasi に渡します。kakasi には入出力の文字コードを指定するオプションがあります。
関数 popen の処理を次のように変更して下さい。変数 $infname は一時ファイル名です(空白文字を含めないようにして下さい)。
 $handle = popen("{$kakasi} -w -ieuc -oeuc < {$infname}", "r");

参考書籍

参考サイト

(この項おわり)
header