php简易中文分词
php简易中文分词
用PHP去做中文分词并不是一个太明智的举动, :p下面是我根据网上找的一个字典档, 简易实现的一个分词程序.
(注: 字典档是gdbm格式, key是词 value是词频, 约4万个常用词)
代码请参见http://www.shi8.com/out/support/art_316.txt
<?php //中文分词系统简易实现办法 //切句单位:凡是ascii值<128的字符 //常见双字节符号:《》,。、?“”;:!¥……%$#@^&*()[]{}|\/"' //可以考虑加入超常见中文字: 的 和 是 不 了 啊 (不过有特殊字比如 "打的" "郑和" .. :p) //计算时间 function getmicrotime(){ list($usec, $sec) = explode(" ",microtime()); return ((float)$usec + (float)$sec); } $time_start = getmicrotime(); //词典类 class ch_dictionary { var $_id; function ch_dictionary($fname = "") { if ($fname != "") { $this->load($fname); } } // 根据文件名载入字典 (gdbm数据档案) function load($fname) { $this->_id = dba_popen($fname, "r", "gdbm"); if (!$this->_id) { echo "failed to open the dictionary.($fname)<br>n"; exit; } } // 根据词语返回频率, 不存在返回-1 function find($word) { $freq = dba_fetch($word, $this->_id); if (is_bool($freq)) $freq = -1; return $freq; } } // 分词类: (逆向) // 先将输入的字串正向切成句子, 然后一句一句的分词, 返回由词组成的数组. class ch_word_split { var $_mb_mark_list; // 常见切分句子的全角标点 var $_word_maxlen; // 单个词最大可能长度(汉字字数) var $_dic; // 词典... var $_ignore_mark; // true or false function ch_word_split () { $this->_mb_mark_list = array(",","","。","!","?",":","……","、","“","”","《","》","(",")"); $this->_word_maxlen = 12; // 12个汉字 $this->_dic = NULL; $this->_ignore_mark = true; } // 设定字典 function set_dic($fname) { $this->_dic = new ch_dictionary($fname); } function set_ignore_mark($set) { if (is_bool($set)) $this->_ignore_mark = $set; } // 将字串切成句子再加以切分成词 function string_split($str, $func = "") { $ret = array(); if ($func == "" || !function_exists($func)) $func = ""; $len = strlen($str); $qtr = ""; for ($i = 0; $i < $len; $i++) { $char = $str[$i]; if (ord($char) < 0xa1) { // 读取到一个半角字符 if (!empty($qtr)) { $tmp = $this->_sen_split($qtr); $qtr = ""; if ($func != "") call_user_func($func, $tmp); else $ret = array_merge($ret, $tmp); } // 如果是单词或数字. 根据 char 将数据读取到 >= 0xa1为止 if ($this->_is_alnum($char)) { do { if (($i+1) >= $len) break; $char2 = substr($str, $i + 1, 1); if (!$this->_is_alnum($char2)) break; $char .= $char2; $i++; } while (1); if ($func != "") call_user_func($func, array($char)); else $ret[] = $char; } elseif ($char == ' ' || $char == "t") { // nothing. continue; } elseif (!$this->_ignore_mark) { if ($func != "") call_user_func($func, array($char)); else $ret[] = $char; } } else { // 双字节字符. $i++; $char .= $str[$i]; if (in_array($char, $this->_mb_mark_list)) { if (!empty($qtr)) { $tmp = $this->_sen_split($qtr); $qtr = ""; if ($func != "") call_user_func($func, $tmp); else $ret = array_merge($ret, $tmp); } if (!$this->_ignore_mark) { if ($func != "") call_user_func($func, array($char)); else $ret[] = $char; } } else { $qtr .= $char; } } } if (strlen($qtr) > 0) { $tmp = $this->_sen_split($qtr); if ($func != "") call_user_func($func, $tmp); else $ret = array_merge($ret, $tmp); } // return value if ($func == "") { return $ret; } else { return true; } } // 将句子切成词, 逆向 function _sen_split($sen) { $len = strlen($sen) / 2; $ret = array(); for ($i = $len - 1; $i >= 0; $i--) { // 如: 这是一个分词程序 // 先取得最后一个字 $w = substr($sen, $i * 2, 2); // 最终的词长 $wlen = 1; // 开始逆向匹配到最大长度. $lf = 0; // last freq for ($j = 1; $j <= $this->_word_maxlen; $j++) { $o = $i - $j; if ($o < 0) break; $w2 = substr($sen, $o * 2, ($j + 1) * 2); $tmp_f = $this->_dic->find($w2); //echo "{$i}.{$j}: $w2 (f: $tmp_f)n"; if ($tmp_f > $lf) { $lf = $tmp_f; $wlen = $j + 1; $w = $w2; } } // 根据 $wlen 将 $i 偏移了 $i = $i - $wlen + 1; array_push($ret, $w); } $ret = array_reverse($ret); return $ret; } // 判断字符是不是 字母数字_- [0-9a-z_-] function _is_alnum($char) { $ord = ord($char); if ($ord == 45 || $ord == 95 || ($ord >= 48 && $ord <= 57)) return true; if (($ord >= 97 && $ord <= 122) || ($ord >= 65 && $ord <= 90)) return true; return false; } } // 分词后的回调函数 function call_back($ar) { foreach ($ar as $tmp) { echo $tmp . " "; //flush(); } } // 实例(如果没有输入就从 sample.txt中读取): $wp = new ch_word_split(); $wp->set_dic("dic.db"); if (!isset($_REQUEST['testdat']) || empty($_REQUEST['testdat'])) { $data = file_get_contents("sample.txt"); } else { $data = & $_REQUEST['testdat']; } // output echo "<h3>简易分词演示</h3>n"; echo "<hr>n"; echo "分词结果(" . strlen($data) . " chars): <br>n<textarea cols=100 rows=10>n"; // 设定是否忽略不返回分词符号(标点,常用字) $wp->set_ignore_mark(false); // 执行切分, 如果没有设置 callback 函数, 则返回由词组成的array $wp->string_split($data, "call_back"); $time_end = getmicrotime(); $time = $time_end - $time_start; echo "</textarea><br>n本次分词耗时: $time seconds <br>n"; ?> <hr> <form method=post> 您也可以在下面文本框中输入文字,提交后试验分词效果:<br> <textarea name=testdat cols=100 rows=10></textarea><br> <input type=submit> </form> <hr> |