[转]用PHP简易实现中文分词

用PHP简易实现中文分词


文章作者:Hightman
文章来自:http://php.twomice.net/show_hdr.php?xname=BORRG11&dname=P7SRG11&xpos=7


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

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

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

完整的程序演示及下载请参见: http://root.twomice.net/my_php4/dict/chinese_segment.php

<?php
//中文分词系统简易实现办法
//切句单位:凡是ascii值<128的字符
//常见双字节符号:《》,。、?“”;:!¥…… %$#@^&*()[]{}|\/"'
//可以考虑加入超常见中文字:的和是不了啊(不过有特殊字比如"打的""郑和"..:p)

//计算时间
functiongetmicrotime(){
list(
$usec,$sec)=explode("",microtime());
return((float)
$usec+(float)$sec);
}
$time_start=getmicrotime();


//词典类
classch_dictionary{
var
$_id;

function
ch_dictionary($fname=""){
if(
$fname!=""){
$this->load($fname);
}
}

//根据文件名载入字典(gdbm数据档案)
functionload($fname){
$this->_id=dba_popen($fname,"r","gdbm");
if(!
$this->_id){
echo
"failedtoopenthedictionary.($fname)<br>\n";
exit;
}
}

//根据词语返回频率,不存在返回-1
functionfind($word){
$freq=dba_fetch($word,$this->_id);
if(
is_bool($freq))$freq=-1;
return
$freq;
}
}

//分词类:(逆向)
//先将输入的字串正向切成句子,然后一句一句的分词,返回由词组成的数组.
classch_word_split{
var
$_mb_mark_list;//常见切分句子的全角标点
var$_word_maxlen;//单个词最大可能长度(汉字字数)
var$_dic;//词典...
var$_ignore_mark;//trueorfalse

functionch_word_split(){
$this->_mb_mark_list=array(","," ","。","!","?",":","……","、","“","”","《","》","(",")");
$this->_word_maxlen=12;//12个汉字
$this->_dic=NULL;
$this->_ignore_mark=true;
}

//设定字典
functionset_dic($fname){
$this->_dic=newch_dictionary($fname);
}

function
set_ignore_mark($set){
if(
is_bool($set))$this->_ignore_mark=$set;
}

//将字串切成句子再加以切分成词
functionstring_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);
}

//returnvalue
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;//lastfreq
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;
}
}


//分词后的回调函数
functioncall_back($ar){
foreach(
$aras$tmp){
echo
$tmp."";
//flush();
}
}

//实例(如果没有输入就从sample.txt中读取):
$wp=newch_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<textareacols=100rows=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本次分词耗时:$timeseconds<br>\n";
?>
<hr>
<formmethod=post>
您也可以在下面文本框中输入文字,提交后试验分词效果:<br>
<textareaname=testdatcols=100rows=10></textarea><br>
<inputtype=submit>
</form>
<hr>
附:<br>
<li>本程序源码:<ahref="chinese_segment.phps">chinese_segment.php</a>(简易实现方式)</li>
<li>需要的字典:<ahref="dic.db">dic.db</a>(gdbm格式)</li>


附:
简易中文分词实现完整代码及字典下载
http://php.twomice.net/show_hdr.php?xname=BORRG11&dname=P7SRG11&xpos=19
C版简易中文分词服务程序(cscwsd)
http://php.twomice.net/show_hdr.php?xname=BORRG11&dname=P7SRG11&xpos=40

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值