当前位置:Gxlcms > PHP教程 > 基于RMM的简易中文分词

基于RMM的简易中文分词

时间:2021-07-01 10:21:17 帮助过:14人阅读

本程序为基于RMM中文分词思想,编写的简易中文分词,程序中还存在不少漏洞,望大神指点....优化了下乱码问题
  1. /**
  2. * 基于RMM中文分词(逆向匹配法)
  3. * @author tangpan
  4. * @date 2013-10-12
  5. * @version 1.0.0
  6. **/
  7. class SplitWord {
  8. //public $Tag_dic = array(); //存储词典分词
  9. public $Rec_dic = array(); //存储重组的分词
  10. public $Split_char = ' '; //分隔符
  11. public $Source_str = ''; //存储源字符串
  12. public $Result_str = ''; //存储分词结果字符串
  13. public $limit_lenght = 2;
  14. public $Dic_maxLen = 28; //词典中词的最大长度
  15. public $Dic_minLen = 2; //词典中词的最小长度
  16. public function SplitWord() { //初始化对象,并自动执行成员方法
  17. $this->__construct();
  18. }
  19. public function __construct() {
  20. $dic_path = dirname(__FILE__).'/words.csv'; //预先载入词典以提高分词速度
  21. $fp = fopen( $dic_path, 'r' ); //读取词库中的词
  22. while( $line = fgets( $fp, 256 ) ) {
  23. $ws = explode(' ', $line); //对词库中的词进行分割
  24. $ws[0] = trim(iconv('utf-8','GBK',$ws[0])); //编码转换
  25. //$this->Tag_dic[$ws[0]] = true; //以词为索引,序号为值
  26. $this->Rec_dic[strlen($ws[0])][$ws[0]] = true; //以词长度和词分别为二维数组的索引,以n为值,来重组词库
  27. }
  28. fclose($fp); //关闭词库
  29. }
  30. /**
  31. * 设置源字符串
  32. * @param 要分词的字符串
  33. */
  34. public function SetSourceStr( $str ) {
  35. $str = iconv( 'utf-8', 'GBK', $str ); // 将utf-8编码字符转换为GBK编码
  36. $this->Source_str = $this->DealStr( $str ); //初步处理字符串
  37. }
  38. /**
  39. * 检查字符串
  40. * @param $str 源字符串
  41. * @return bool
  42. */
  43. public function checkStr( $str ) {
  44. if ( trim($str) == '' ) return; //若字符串为空,直接返回
  45. if ( ord( $str[0] ) > 0x80 ) return true; //是中文字符则返回true
  46. else return false; //不是中文字符则返回false
  47. }
  48. /**
  49. * RMM分词算法
  50. * @param $str 待处理字符串
  51. */
  52. public function SplitRMM( $str = '' ) {
  53. if ( trim( $str ) == '' ) return; //若字符串为空,则直接返回
  54. else $this->SetSourceStr( $str ); //字符串不为空时,设置源字符串
  55. if ( $this->Source_str == ' ' ) return; //当源字符串为空时,直接返回
  56. $split_words = explode( ' ', $this->Source_str ); //以空格来切分字符串
  57. $lenght = count( $split_words ); //计算数组长度
  58. for ( $i = $lenght - 1; $i >= 0; $i-- ) {
  59. if ( trim( $split_words[$i] ) == ' ' ) continue; //如果字符为空时,跳过后面的代码,直接进入下一次循环
  60. if ( $this->checkStr( $split_words[$i] ) ) { //检查字符串,如果是中文字符
  61. if ( strlen( $split_words[$i] ) >= $this->limit_lenght ) { //字符串长度大于限制大小时
  62. //对字符串进行逆向匹配
  63. $this->Result_str = $this->pregRmmSplit( $split_words[$i] ).$this->Split_char.$this->Result_str;
  64. }
  65. } else {
  66. $this->Result_str = $split_words[$i].$this->Split_char.$this->Result_str;
  67. }
  68. }
  69. $this->clear( $split_words ); //释放内存
  70. return iconv('GBK', 'utf-8', $this->Result_str);
  71. }
  72. /**
  73. * 对中文字符串进行逆向匹配方式分解
  74. * @param $str 字符串
  75. * @return $retStr 分词完成的字符串
  76. */
  77. public function pregRmmSplit( $str ) {
  78. if ( $str == ' ' ) return;
  79. $splen = strlen( $str );
  80. $Split_Result = array();
  81. for ( $j = $splen - 1; $j >= 0; $j--) { //逆向匹配字符
  82. if ( $splen <= $this->Dic_minLen ) { //当字符长度大于词典中最小字符长度时
  83. if ( $j == 1 ) { //当长度为 1 时
  84. $Split_Result[] = substr( $str, 0, 2 );
  85. } else {
  86. $w = trim( substr( $str, 0, $this->Dic_minLen + 1 ) ); //截取前四个字符
  87. if ( $this->IsWord( $w ) ) { //判断词典中是否存在该字符
  88. $Split_Result[] = $w; //存在,则写入数组存储
  89. } else {
  90. $Split_Result[] = substr( $str, 2, 2 ); //逆向存储
  91. $Split_Result[] = substr( $str, 0, 2 );
  92. }
  93. }
  94. $j = -1; //关闭循环;
  95. break;
  96. }
  97. if ( $j >= $this->Dic_maxLen ) $max_len = $this->Dic_maxLen; //当字符长度大于词典最大词的长度时,赋值最大限制长度
  98. else $max_len = $j;
  99. for ( $k = $max_len; $k >= 0; $k = $k - 2 ) { //一次跳动为一个中文字符
  100. $w = trim( substr( $str, $j - $k, $k + 1 ) );
  101. if ( $this->IsWord( $w ) ) {
  102. $Split_Result[] = $w; //保存该词
  103. $j = $j - $k - 1; //位置移动到已匹配的字符的位置
  104. break; //分词成功即跳出当前循环,进入下一循环
  105. }
  106. }
  107. }
  108. $retStr = $this->resetWord( $Split_Result ); //重组字符串,并返回处理好的字符串
  109. $this->clear( $Split_Result ); //释放内存
  110. return $retStr;
  111. }
  112. /**
  113. * 重新识别并组合分词
  114. * @param $Split_Result 重组目标字符串
  115. * @return $ret_Str 重组字符串
  116. */
  117. public function resetWord( $Split_Result ) {
  118. if ( trim( $Split_Result[0] ) == '' ) return;
  119. $Len = count( $Split_Result ) - 1;
  120. $ret_Str = '';
  121. $spc = $this->Split_char;
  122. for ( $i = $Len; $i >= 0; $i-- ) {
  123. if ( trim( $Split_Result[$i] ) != '' ) {
  124. $Split_Result[$i] = iconv( 'GBK', 'utf-8', $Split_Result[$i] );
  125. $ret_Str .= $spc.$Split_Result[$i].' ';
  126. }
  127. }
  128. //$ret_Str = preg_replace('/^'.$spc.'/','、',$ret_Str);
  129. $ret_Str = iconv('utf-8','GBK',$ret_Str);
  130. return $ret_Str;
  131. }
  132. /**
  133. * 检查词典中是否存在某个词
  134. * @param $okWord 检查的词
  135. * @return bool;
  136. */
  137. public function IsWord( $okWord ) {
  138. $len = strlen( $okWord );
  139. if ( $len > $this->Dic_maxLen + 1 ) return false;
  140. else { //根据二维数组索引匹配,是否存在该词
  141. return isset($this->Rec_dic[$len][$okWord]);
  142. }
  143. }
  144. /**
  145. * 初步处理字符串(以空格来替换特殊字符)
  146. * @param $str 要处理的源字符串
  147. * @return $okStr 返回预处理好的字符串
  148. */
  149. public function DealStr( $str ) {
  150. $spc = $this->Split_char; //拷贝分隔符
  151. $slen = strlen( $str ); //计算字符的长度
  152. if ( $slen == 0 ) return; //如果字符长度为0,直接返回
  153. $okstr = ''; //初始化变量
  154. $prechar = 0; //字符判断变量(0-空白,1-英文,2-中文,3-符号)
  155. for ( $i = 0; $i < $slen; $i++ ) {
  156. $str_ord = ord( $str[$i] );
  157. if ( $str_ord < 0x81 ) { //如果是英文字符
  158. if ( $str_ord < 33 ) { //英文的空白符号
  159. if ( $str[$i] != '\r' && $str[$i] != '\n' )
  160. $okstr .= $spc;
  161. $prechar = 0;
  162. continue;
  163. } else if ( ereg('[@\.%#:\^\&_-]',$str[$i]) ) { //如果关键字的字符是数字或英文或特殊字符
  164. if ( $prechar == 0 ) { //当字符为空白符时
  165. $okstr .= $str[$i];
  166. $prechar = 3;
  167. } else {
  168. $okstr .= $spc.$str[$i]; //字符不为空白符时,在字符前串上空白符
  169. $prechar = 3;
  170. }
  171. } else if ( ereg('[0-9a-zA-Z]', $str[$i]) ) { //分割英文数字组合
  172. if ( (ereg('[0-9]',$str[$i-1]) && ereg('[a-zA-Z]',$str[$i]))
  173. || (ereg('[a-zA-Z]',$str[$i-1]) && ereg('[0-9]',$str[$i])) ) {
  174. $okstr .= $spc.$str[$i];
  175. } else {
  176. $okstr .= $str[$i];
  177. }
  178. }
  179. } else { //如果关键字的第二个字符是汉字
  180. if ( $prechar != 0 && $prechar != 2 ) //如果上一个字符为非中文和非空格,则加一个空格
  181. $okstr .= $spc;
  182. if ( isset( $str[$i+1] ) ) { //如果是中文字符
  183. $c = $str[$i].$str[$i+1]; //将两个字符串在一起,构成一个中文字
  184. $n = hexdec( bin2hex( $c ) ); //将ascii码转换成16进制,再转化为10进制
  185. if ( $n > 0xA13F && $n < 0xAA40 ) { //如果为中文标点符号
  186. if ( $prechar != 0 ) $okstr .= $spc; //将中文标点替换为空
  187. //else $okstr .= $spc; //若前一个字符为空,则直接串上
  188. $prechar = 3;
  189. } else { //若不是中文标点
  190. $okstr .= $c;
  191. $prechar = 2;
  192. }
  193. $i++; // $i 再加 1 ,即使一次移动为一个中文字符
  194. }
  195. }
  196. }
  197. return $okstr;
  198. }
  199. /**
  200. * 释放内存
  201. * @param $data 暂存数据
  202. */
  203. public function clear( $data ) {
  204. unset( $data ); //删除暂存数据
  205. }
  206. }
  207. ?>

人气教程排行