当前位置: 洪哥笔记 > PHP > php简易中文分词

 

php简易中文分词


关键词

php简易中文分词

摘要

用PHP去做中文分词并不是一个太明智的举动, :p,下面是我根据网上找的一个字典档, 简易实现的一个分词程序。

用PHP去做中文分词并不是一个太明智的举动, :p

下面是我根据网上找的一个字典档, 简易实现的一个分词程序.

(注: 字典档是gdbm格式, key是词 value是词频, 约4万个常用词)

<?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>

 

要饭二维码

洪哥写文章很苦逼,如果本文对您略有帮助,可以扫描下方二维码支持洪哥!金额随意,先行谢过!大家的支持是我前进的动力!

文章的版权

本文属于“洪哥笔记”原创文章,转载请注明来源地址:php简易中文分词:http://www.splaybow.com/post/zwfc09492611122007.html

如果您在服务器运维、网络管理、网站或系统开发过程有需要提供收费服务,请加QQ:115085382!十年运维经验,帮您省钱、让您放心!
亲,如果有需要,先存起来,方便以后再看啊!加入收藏夹的话,按Ctrl+D

« 如何学习php PHP获取网卡MAC地址 »