当前位置:首页 > 用PHP简易实现中文分词

用PHP简易实现中文分词

点击次数:1226  更新日期:2011-01-03
\n

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


\n

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


\n

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


\n

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


\n

\n

//计算时间
function getmicrotime(){
list(usec, sec) = explode(” “,microtime());
return ((float)usec + (float)sec);
}
time_start = getmicrotime();


\n


//词典类
class ch_dictionary {
var _id;


\n

function ch_dictionary(fname = “”) {
if (fname != “”) {
this->load(fname);
}
}


\n

// 根据文件名载入字典 (gdbm数据档案)
function load(fname) {
this->_id = dba_popen(fname, “r”, “gdbm”);
if (!this->_id) {
echo “failed to open the dictionary.(fname)<br>\\n”;
exit;
}
}


\n

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


\n

// 分词类: (逆向)
// 先将输入的字串正向切成句子, 然后一句一句的分词, 返回由词组成的数组.
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;
}


\n

// 设定字典
function set_dic(fname) {
this->_dic = new ch_dictionary(fname);
}


\n

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


\n

// 将字串切成句子再加以切分成词
function string_split(str, func = “”) {
ret = array();

if (func == “” || !function_exists(func)) func = “”;

len = strlen(str);
qtr = “”;


\n

for (i = 0; i < len; i++) {
char = str[i];


\n

if (ord(char) < 0xa1) {
// 读取到一个半角字符
if (!empty(qtr)) {
tmp = this->_sen_split(qtr);
qtr = “”;


\n

if (func != “”) call_user_func(func, tmp);
else ret = array_merge(ret, tmp);
}


\n

// 如果是单词或数字. 根据 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;


\n

char .= char2;
i++;
} while (1);


\n

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 = “”;


\n

if (func != “”) call_user_func(func, tmp);
else ret = array_merge(ret, tmp);
}


\n

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);


\n

if (func != “”) call_user_func(func, tmp);
else ret = array_merge(ret, tmp);
}


\n

// return value
if (func == “”) {
return ret;
}
else {
return true;
}
}


\n

// 将句子切成词, 逆向
function _sen_split(sen) {
len = strlen(sen) / 2;
ret = array();


\n

for (i = len – 1; i >= 0; i–) {
// 如: 这是一个分词程序

// 先取得最后一个字
w = substr(sen, i * 2, 2);


\n

// 最终的词长
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);
}


\n

ret = array_reverse(ret);
return ret;
}


\n

// 判断字符是不是 字母数字_- [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;
}
}


\n


// 分词后的回调函数
function call_back(ar) {
foreach (ar as tmp) {
echo tmp . ” “;
//flush();
}
}


\n

// 实例(如果没有输入就从 sample.txt中读取):
wp = new ch_word_split();
wp->set_dic(“dic.db”);


\n

if (!isset(_REQUEST[\'testdat\']) || empty(_REQUEST[\'testdat\'])) {
data = file_get_contents(“sample.txt”);
}
else {
data = & _REQUEST[\'testdat\'];
}


\n

// output
echo “<h3>简易分词演示</h3>\\n”;
echo “<hr>\\n”;
echo “分词结果(” . strlen(data) . ” chars): <br>\\n<textarea cols=100 rows=10>\\n”;


\n

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


\n

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


\n

time_end = getmicrotime();
time = time_end – time_start;


\n

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>

\n