MeCabのクラス

提供:wiki - PCスキルの小技・忘却防止メモ
移動: 案内, 検索

ワカチをするときに使うMeCabを利用。

ソース

随分前に用意したクラスなので詳細不明。コメントを読みながらガンバル。確か、コンソールから実行時のみ使えたような気がする。

<?php
 /**
  * my_wakachi.class.php
  * MeCabのクラス。(MeCab extensionを使わない)
  * ver 1.0 2011/06/13
  */
 class ClassMecab{
 	function __construct(){}
 	
 	function __destruct(){}
 	
 	/**
 	 * executeMecab() - コマンドラインでMeCabを実行
 	 * @param str $command MeCabを実行するコマンド
 	 * @return str MeCabの実行結果
 	 */
 	function executeMecab($command){
 		$rstMcb = `$command`;
 		return $rstMcb;
 	}
 	
 	/**
 	 * changeArrayStr() - MeCabの実行結果を配列に変換
 	 */
 	function changeArrayResult( $str ){
 		// フッタの削除
 		$temp = $this->dellFooter($str);
 		
 		// コマンドラインでの実行結果を配列へ。
 		$arys = explode("\n", $temp);
 		
 		// 品詞の中から、名詞のみ特殊処理:複合名詞作成
 		$arys = $this->creatCompoundNoun($arys);
 		
 		return $arys;
 	}
 	
 	/**
 	 * getNounFromSpeech() - 品詞の中から、名詞のみ抽出
 	 * @param ary $arys 品詞一覧
 	 * @return ary キーが品詞、値が個数
 	 */
 	function getNounFromSpeech($arys){
 		foreach ($arys as $k=>$v) {
 			$splt = explode(",", $k);
 			#if( !strstr($splt[2], "名詞") ){
 			if( preg_match('/^名詞/u', $splt[2] ) && !( $splt[2] === "名詞-サ変接続" && $splt[0] === 1 ) ){
 				// 名詞の場合。但し、名詞-サ変接続で、未知語($splt[0]=1)は除く。
 			}
 			else{
 				// 名詞を含まない場合は削除
 				unset($arys[$k]);
 			}
 		}
 		return $arys;
 	}
 	
 	/**
 	 * creatCompoundNoun() - 品詞の中から、名詞のみ特殊処理:複合名詞作成。他はそのまま
 	 * @param ary $pSpeech 品詞一覧
 	 * @return ary キーが品詞、値が個数
 	 */
 	function creatCompoundNoun($pSpeech){
 		
 		// 配列の初期化
 		$retAry = array();
 		
 		$i = 0;
 		foreach( $pSpeech as $spVal ){
 			// 複合名詞の処理
 			$splt = explode(",", $spVal);
 			
 			if( count($splt) !== 3 ){
 				// 変に残った空白行
 				continue;
 			}
 			
 			// 空白文字から始まるなら空白文字削除
 			$dllSpc = preg_replace('/^[ ]+/u', "", $splt[1]);
 			$delVal = $splt[0] . "," . $dllSpc . "," . $splt[2];
 			
 			$prtoSp = ( count($splt) >= 3 )? $splt[2]: "";
 			
 			if( preg_match('/^名詞/u', $prtoSp ) && !( $prtoSp === "名詞-サ変接続" && $splt[0] === 1 ) ){
 				// 名詞の場合。但し、名詞-サ変接続で、未知語($splt[0]=1)は除く。
 				
 				if( $i !== 0 ){
 					// ループの1つ目ではない場合
 					
 					$j = count($retAry) - 1;
 					$pref = explode(",", $retAry[$j]);
 					
 					if( preg_match('/^名詞/u', $pref[2] ) ){
 						// 前回が名詞の場合
 						
 						if( preg_match('/[ ]$/u', $retAry[$j] ) ){
 							// 前回が全角スペースで終っていたなら別の名詞に。
 							
 							// 前回の単語の最後のスペースを削除して修正
 							$retAry[$j]  = $this->rewriteDellSpace($retAry[$j]);
 							
 							// 今回の名詞を追加
 							$retAry[] .= $delVal;
 						}
 						else{
 							$retAry[$j]  = $pref[0] . ",";
 							$retAry[$j] .= $pref[1] . $dllSpc . ",";
 							$retAry[$j] .= $pref[2];
 						}
 					}
 					else{
 						// 前回が名詞ではない場合
 						
 						// 前回の単語の最後のスペースを削除して修正
 						$retAry[$j]  = $this->rewriteDellSpace($retAry[$j]);
 						
 						// 今回の単語を追加
 						$retAry[]  = $delVal;
 					}
 				}
 				else{
 					// ループの1つ目の場合
 					$retAry[] = $delVal;
 				}
 			}
 			else{
 				// その他
 				if( $i !== 0 ){
 					// ループの1つ目ではない場合
 					$j = count($retAry) - 1;
 					
 					// 前回の単語の最後のスペースを削除して修正
 					$retAry[$j]  = $this->rewriteDellSpace($retAry[$j]);
 				}
 				
 				// 今回の単語を追加
 				$retAry[] = $delVal;
 			}
 			$i++;
 			unset($splt,$dllSpc,$delVal,$prtoSp);
 		}
 		
 		$retAry = array_count_values($retAry);
 		return $retAry;
 	}
 	
 	/**
 	 * rewriteDellSpace() - 前回の名詞の最後のスペースを削除して修正
 	 * @param str $tmp 対象の文字列
 	 */
 	function rewriteDellSpace( $tmp ){
 		$pref = explode(",", $tmp);
 		$pref[1] = preg_replace('/[ ]+$/u', "", $pref[1]);
 		$ret = $pref[0] . "," . $pref[1] . "," . $pref[2];
 		unset($pref);
 		return $ret;
 	}
 	
 	/**
 	 * dellFooter() - MeCabの実行結果からフッタ部を削除
 	 * @param str $result MeCabの実行結果
 	 */
 	function dellFooter( $result ){
 		$result = preg_replace('/\nEOS\n/', "", $result);
 		return $result;
 	}
 	
 	/**
 	 * makeCommandMecab() - MeCabのコマンド作成(品詞一覧の取得)
 	 * @param str $str 調査対象の文字列
 	 * @param str $opt MeCabの実行する際のオプション
 	 * @return str MeCabのコマンド
 	 */
 	function makeCommandMecab($str, $opt){
 		// 特殊文字のエスケープ
 		$str = escapeSpecialCharAtMecab($str);
 		
 		// コマンドライン上で実行するコマンド作成
 		$runMcb = "echo " . $str . " | mecab" . $opt . " -O parse";
 		
 		return $runMcb;
 	}
 	
 	/**
 	 * mergePartOfSpeech() - 配列の合体
 	 * @param ary $toAry 品詞一覧
 	 * @param ary $fromAry $toAryへ追加する品詞
 	 */
 	function mergePartOfSpeech($toAry, $fromAry){
 		foreach( $fromAry as $addK=>$addV ){
 			if( isset($toAry[$addK]) ){
 				// すでにキーが存在する場合は$addV追加
 				$toAry[$addK] += $addV;
 			}
 			else{
 				// キーが存在しない場合は$addVを設定
 				$toAry[$addK] = $addV;
 			}
 		}
 		return $toAry;
 	}
 	
 	/**
 	 * getAllNoun() - 文章から名詞のみ抽出
 	 * @param ary $arys 文章を分割したもの
 	 * @return ary キーに名詞情報、値に個数
 	 */
 	function getAllNoun($arys){
 		$nouns = array();
 		
 		foreach ( $arys as $v ){
 			// 分割した文字数のバッファチェックとオプション作成:状況によっては修正する
 			if( ($opt = $this->chkBuffer($v)) !== FALSE ){
 				// MeCabのコマンド作成(品詞一覧の取得)
 				$runMcb = $this->makeCommandMecab($v, $opt);
 				
 				// コマンドライン上でMeCabを実行
 				$temp = $this->executeMecab($runMcb);
 				
 				// 実行結果を配列に変換
 				$tempNouns = $this->changeArrayResult($temp);
 				
 				// 品詞の中から、名詞のみ抽出
 				$tempNouns = $this->getNounFromSpeech($tempNouns);
 				
 				// 戻り値用に配列の合体
 				$nouns = $this->mergePartOfSpeech($nouns, $tempNouns);
 			}
 			else{
 				echo "バッファチェックで不思議なことが…\n";
 				exit();
 			}
 			unset($runMcb, $v, $tempNouns, $temp);
 		}
 		return $nouns;
 	}
 	
 	/**
 	 * countNounAll() - 名詞総数の取得
 	 * @param ary $words 単語群
 	 */
 	function countNounAll($words){
 		$num = 0;
 		foreach ($words as $v){
 			$num += $v;
 		}
 		return $num;
 	}
 	
 	/**
 	 * getNumNoun() - 名詞数の取得
 	 * @param str $str 対象のテキスト
 	 */
 	function getNumNoun($str){
 		if( !isset($str) || $str === "" ){
 			#echo "\n対象のテキスト不明\n";
 			return NULL;
 			#exit();
 		}
 		
 		// 長すぎるとエラー:不思議なことになるので適当に分割
 		$arys = $this->splitSentence($str, 25000);
 		
 		// 文章から名詞のみ抽出
 		$nouns = $this->getAllNoun($arys);
 		
 		// 名詞の総数の取得
 		$num = $this->countNounAll($nouns);
 		
 		return $num;
 	}
 	
 	/**
 	 * chkBuffer() - 分割した文字列のバッファ(文字数?)チェック
 	 * @param str $str 調査対象の文字列
 	 * @return str MeCabを実行する際のオプション
 	 */
 	function chkBuffer($str){
 		// 入力文字列のバッファサイズが多いとエラーになる。デフォルト値は8192byte。maxは(8192*640)byte
 		$len = strlen($str);
 		$mBf = 8192 * 640;
 		
 		if( $len > 8192 ){
 			if( $len > $mBf ){
 				// MAX値を超えたら調査しない。
 				echo "\n" . 'over MAX : $len:' . $len . "\n";
 				exit();
 			}
 			else{
 				$opt_b = " -b " . $mBf;
 			}
 		}
 		else{
 			$opt_b = "";
 		}
 		return $opt_b;
 	}
 	
 	/**
 	 * splitSentence() - 文章を分割する(「指定した文字数+空白文字まで」で分割)
 	 * @param str $str 分割したい文章
 	 * @param int $num 適当に分割する際の文字数(指定無しで25000文字)
 	 * @return ary
 	 */
 	function splitSentence($str, $num=25000){
 		// 無理矢理区切るように色々としておく。半角スペースいれておく
 		$str = preg_replace('/ /', " ", $str);
 		$str = preg_replace('/(。|、) */', "$1 ", $str);
 		
 		// 長すぎるとエラー:不思議なことになるので適当に分割
 		$cntChar = mb_strlen($str); // 分割したい文章の文字数
 		$restStr = $str;            // 作業用
 		
 		$sepStr = array();
 		do{
 			if(mb_strlen($restStr) > $num){
 				// 文字数が多いとき。
 				
 				// 最初から指定した文字数まで取得
 				$tempOne = mb_substr($restStr, 0, $num);
 				
 				// 残りの文章の取得($tempOneを除く)
 				$restStr = mb_substr($restStr, mb_strlen($tempOne));
 				
 				// 残りの文章から空白までを追加
 				if( strstr($restStr, " ") ){
 					$tempTwo = getBetweenFirstAndWord($restStr, " ");
 				}
 				else{
 					// 残りの文章に空白が無ければ最後まで取得する
 					$tempTwo = $restStr;
 				}
 				
 				// 残りの文章の取得($tempTwoを除く)
 				$restStr = mb_substr($restStr, mb_strlen($tempTwo));
 				
 				// 前後の空白を削除して配列に。
 				$sepStr[] = trim( $tempOne . $tempTwo );
 				unset($tempOne, $tempTwo);
 			}
 			else{
 				// 文字数が少ないとき。
 				$sepStr[] = trim( $restStr );
 				$restStr = "";
 			}
 		}while( mb_strlen($restStr) > 0 );
 		
 		return $sepStr;
 	}
 }
 ?>

関連項目