読者です 読者をやめる 読者になる 読者になる

カロリー計算〜その2〜

http://d.hatena.ne.jp/ymlab/20120102

の続き。
今度は、これをシステムの方で検索しやすいようにしなければならない。
日本語は超面倒で漢字や日本語があるため、ややこしい。
例えば、みかん一つにしても、

みかん
蜜柑
ミカン
オレンジ(←ちょっと強引か)

の全てにヒットする必要がある。
これは難しすぎる。
そこで、とりあえず携帯電話のアカサタナ検索みたいに、全てをひらがな検索すればいいんじゃね?目次みたいに。

ってことで、エクセルを起動し、名称の右に列を追加し、一つ一つ手入力・・・、できるわけがない。
そこで、mecab を使おうと思った。

mecab   京都大学情報学研究科−日本電信電話株式会社コミュニケーション科学基礎研究所 
共同研究ユニットプロジェクトを通じて開発されたオープンソース形態素解析エンジン。
言語, 辞書,コーパスに依存しない汎用的な設計を 基本方針としています。 パラメータの
推定に Conditional Random Fields (CRF) を用 いており, ChaSenが採用している 
隠れマルコフモデルに比べ性能が向上しています。また、平均的に ChaSen, Juman,
 KAKASIより高速に動作します。 ちなみに和布蕪(めかぶ)は, 作者の好物です。
http://mecab.sourceforge.net/

だ、そうである。

で、段々記事を書くのが面倒になってきたので(備忘録のために書いているが早く開発に戻りたい。)、
そのためのスクリプトを記述する。本気で保守する気もないので全くリファクタリングもしていない。

日本語文を入力すると、それにふりがなをつけた値を返す。
一応、WebAPI化させているので、ちょっとこざかしいこともしている。
http://ymlabo.ddo.jp/~ymlab/webservice/mecab/mecab_kanji2furigana.php?str=漢字

<?php /* vim: set fdm=marker: */

$str="";
if ( count( $_REQUEST['str'] ) != 0 ) {
	$str = mb_convert_encoding($_REQUEST['str'],"EUC","auto");
	$str = htmlspecialchars($str,ENT_QUOTES);
}

$from = 1;//0 is console. 1 is browser.



if ( strlen($str) == 0 ) {
	//this is not Web service
	$str = $argv[1];
	$str = mb_convert_encoding($str, "EUC", "auto");
	if ( $argc==1 ) {
		print "Usage ./php mecab_kanji2furigana.php nihongobun\n";
	}
	$from = 0;
}
//print "||".rawurlencode($stra)."||";

//print "str = $str | grade = $grade";

/** マルチバイト文字列を1文字ずつ配列に分割する
 * @see http://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q1417635014
 * */
function mbStringToArray( $sStr, $sEnc='UTF-8'){ /**{{{*/
	$aRes = array();
	while($iLen = mb_strlen( $sStr, 'SJIS')) {
		array_push($aRes, mb_substr( $sStr, 0, 1, $sEnc));
		$sStr = mb_substr( $sStr, 1, $iLen, $sEnc );
	}
	return $aRes;
}
/*}}}*/

/** Mecabライブラリを走らせる.*/
function runMecab( $str ) {
	$ret = "";

	exec ("/Users/ymlab/Sites/webservice/mecab/do_mecab.sh ". escapeshellarg($str), &$ary);
	//print "escapeshellarg=[".escapeshellarg($str)."]";
	//exec ("/Users/ymlab/Sites/webservice/mecab/do_mecab.sh ". $str, &$ary);
	$isAlpha = false;
	//print_r($ary);
	foreach ( $ary as $value) {
		//camma check
		if ( $value[0] == "," ) {
			$kanzi = ",";
			$hurigana = "";
		} else {
			$parse = explode(",", $value);
			$kanzi    = substr($parse[0], 0, strpos($parse[0], "\t"));
			$hurigana = $parse[7];
		}
		//print $parse[7];//フォーマットはこんな感じなので。[39] => 練習        名詞,サ変接続,*,*,*,*,練習,レンシュウ,レンシュー
		//print $kanzi;

		//学年でのチェックを入れることにする
	//	if (  !mb_ereg("[あ-ん]|[ア-ン]|[0-9]", $kanzi)) {
			$ret .= $hurigana;
	//	} else {
			/*//自分の単語が英数字の場合は、一文字スペースを入れておく。
			//[そうしないと、二つ以上英単語が並んだとき、くっついてしまう.]
			$isUrl = false;
			if ( mb_ereg("[a-z]|[A-Z]|[0-9]",$kanzi )) {
				if ( $isAlpha == true && $isUrl == false) {
					$ret .= " ";
				}
				$isAlpha = true;
			} else {
				$isAlpha = false;
			}
			$ret .= $kanzi;
		}
		*/
	}
	return $ret;
}


/** 与えられた文字列から、該当する学年に対応したルビを振ったデータを返す
 * @param str 変換する文字列
 * @return ret 変換された文字列
 */
//function translation($str,$from, $encode='ISO-8859-1'){ /*{{{*/
function translation($str, $from, $encode='EUC-JP'){ /*{{{*/
	$ret = "";
	//print "I'm in translation\n";
	//URLはstrに通さないように
	preg_match_all('/[^ -~]+|[ -~]+/', $str, $aMatches);
	for ( $i = 0 ;$i < count($aMatches[0]); $i++ ) {
		$str = $aMatches[0][$i];
		//print "$str";
		//漢字系でなかったら、そのままreturnする.
		if ( preg_match('/[ -~]+/', $str) ) {
			$ret .= $str;
		} else {
			$aaa = runMecab($str);
			$ret .= $aaa;
		}
		//print "ret=$ret<br />";
	}

	$ret =  mb_convert_kana($ret, 'KVc', mb_detect_encoding($ret, 'ASCII,JIS,UTF-8,EUC-JP,SJIS'));
	if ( $from == 1 ) {
		$ret = mb_convert_encoding($ret, "SJIS", "auto");
	}
	print $ret;
	$sEnc = mb_detect_encoding($sTest, 'ASCII,JIS,UTF-8,EUC-JP,SJIS');
	$a2 = mbStringToArray($sTest, $sEnc);
}
/**}}}*/
translation($str, $from);
?>


これで、漢字交じり文をひらがなにすることができるので、いよいよCSVファイルを落とし込もう。
適当に、このプログラムはputhurigana.php とかにしておいて、18回CSVを落とし込む。

$ php puthurigana.php 01.csv >h01.csv &
$ php puthurigana.php 02.csv >h02.csv &
$ php puthurigana.php 03.csv >h03.csv &

という感じ。ここ手入力の方が早そうだったので、手入力していった。

<?php
if ( $argc == 1 ) {
	fputs(STDERR, "usage $php puthurigana.php [filename]");
	exit;
}

$filename = $argv[1];
//fputs(STDERR,"filename = $filename\n");
$fp = @fopen( $filename, "r" );
$ret = "";
while( !feof( $fp ) ) {
	$row = fgets($fp, 1000);
	$arrayRow = explode(",", $row);
	//0〜1まで
	for ( $i=0; $i < 2; $i++ ) {
		$ret.= mb_convert_encoding($arrayRow[$i].",", "EUC-JP", "SJIS");
	}
	//mecab
		$arrayRow[1] = mb_convert_encoding($arrayRow[1], "EUC-JP", "SJIS");
//		fputs(STDERR, "arrayRow[1]=$arrayRow[1]\n");
		exec("php ../mecab_kanji2furigana.php ". escapeshellarg($arrayRow[1]), &$ary );
		//print_r($ary);
		$huriganaed = $ary[2];
		$ret.= "$huriganaed,";
		$ary="";//aryを初期化する.
	//その後の処理
	for ( ;$i < count($arrayRow)-1;$i++ ) {
		$ret.= mb_convert_encoding($arrayRow[$i].",", "EUC-JP", "SJIS");
	}
	$ret.= $arrayRow[$i];
	//print mb_convert_encoding($row, "EUC-JP", "SJIS");
}
print mb_convert_encoding($ret, "SJIS", "EUC");
fclose($fp);


?>

あとは、mecabが誤変換したところを修正していく作業をしていこう。