用PHP简易实现中文分词

本文介绍了一个使用PHP实现的简易中文分词程序。该程序基于一个包含大约4万个常用词的gdbm格式字典,通过逆向匹配的方法进行分词,并提供了完整的代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


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

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

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

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

<?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>
附: <br>
<li>本程序源码: <a href="chinese_segment.phps">chinese_segment.php</a> (简易实现方式)</li>
<li>需要的字典: <a href="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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值