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

news/2024/7/7 16:28:19

 

用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

//中文分词系统简易实现办法
//切句单位:凡是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)
/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 11
);
                        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 22
);

            
// 最终的词长
            
$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 "

简易分词演示

/n" ;
echo 
"
/n"
;
echo 
"分词结果(" strlen($data) . " chars): 
/n /n"
;

// 设定是否忽略不返回分词符号(标点,常用字)
$wp->set_ignore_mark(false
);

// 执行切分, 如果没有设置 callback 函数, 则返回由词组成的array
$wp->string_split($data"call_back"
);

$time_end getmicrotime
();
$time $time_end $time_start
;

echo 
"
/n本次分词耗时: $time seconds 
/n"
;
?>



您也可以在下面文本框中输入文字,提交后试验分词效果:







附: 

  • 本程序源码:  chinese_segment.php (简易实现方式)

  • 需要的字典:  dic.db (gdbm格式)

 


附:
简易中文分词实现完整代码及字典下载
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

 





http://www.niftyadmin.cn/n/3652460.html

相关文章

[转]使用Memcached进行内存缓存

使用Memcached进行内存缓存通常的网页缓存方式有动态缓存和静态缓存等几种&#xff0c;在ASP.NET中已经可以实现对页面局部进行缓存&#xff0c;而使用memcached的缓存比ASP.NET的局部缓存更加灵活&#xff0c;可以缓存任意的对象&#xff0c;不管是否在页面上输出。而memcache…

[转]Linux下缓存服务器Memcached的应用

Linux下缓存服务器的应用摘要&#xff1a;由于数据库存储的数据量越来越大&#xff0c;查询速度也就变的越来越慢&#xff0c;因此就有了缓存服务器应用的必要&#xff0c;本文是介绍Memcached的安装以及简单的使用本文只介绍memcached的PHP的API&#xff0c;想查看其他关于Mem…

数据库(一):数据定义

数据定义 本人这学期在上数据库的课程&#xff0c;对SQL语言的查询功能有点头疼&#xff0c;所以决定写个笔记总结一下SQL实操相关的内容&#xff0c;也当作是在做期末复习材料吧。 参考书目&#xff1a;《数据库系统概论》第5版 王珊著 实验环境&#xff1a;ORACLE 实验语言&…

使用PHP简单网页抓取和内容分析

没事的时候想抓取点网页看看&#xff0c;也不太懂&#xff0c;只是玩玩&#xff0c;写了点代码&#xff0c;聊以娱乐。稍微有点意义的函数是&#xff1a;get_content_by_socket(), get_url(), get_content_url(), get_content_object 几个函数&#xff0c;也许能够给你点什么想…

解决JSP出错:HTML中的列表不能正常显示

项目场景&#xff1a; JSP搭建在线图书管理系统。 问题描述&#xff1a; 首页的推荐图书列表不能正常显示&#xff0c;模糊查询目标书籍的搜索结果列表、借阅排行榜列表也不能正常显示。调用Dao中的方法&#xff0c;可以得到数据&#xff0c;数据库连接正常。推测为前端列表显…

数据库(二):数据查询之单表查询

单表查询 单表查询是指仅涉及一个表的查询。 参考书目&#xff1a;《数据库系统概论》第5版 王珊著 实验环境&#xff1a;ORACLE 实验语言&#xff1a;SQL 文章目录单表查询前言一、选择表中的若干列1.查询指定列2.查询全部列3.查询经过计算的值二、选择表中的若干元组1.消除取…

[转] 使用 DHTML 与 XML 制作 Ajax 幻灯片

使用 DHTML 与 XML 制作 Ajax 幻灯片Jack Herrington (jack_d_herringtoncodegeneration.net), 高级软件工程师, Code Generation Network2006 年 5 月 23 日学习如何创建通过 “Ken Burns Effects” 实现动画的 Ajax 客户端幻灯片放映。通过本文&#xff0c;您将了解如何创建 …

数据库(六):数据查询之基于派生表的查询

基于派生表的查询 子查询不仅可以出现在WHERE子句中&#xff0c;还可以出现在FROM子句中&#xff0c;此时子查询生成的临时派生表成为主查询的查询对象。 参考书目&#xff1a;《数据库系统概论》第5版 王珊著 实验环境&#xff1a;ORACLE、MySQL、Navicat 实验语言&#xff1a…