仓酷云

 找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 1497|回复: 20
打印 上一主题 下一主题

[学习教程] PHP教程之用PHP简略单纯完成中文分词

[复制链接]
透明 该用户已被删除
跳转到指定楼层
楼主
发表于 2015-2-4 00:09:57 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
我先解释一下我的学习思路。中文   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



刚开始觉得自己对这些多少有些基础,很简单,但是看了老师那么熟练的进行网页布局的时候,突然之间发现,其实,我的基础并没有自己想像的那么好,自己设计的页面其实并不好看,就连表格的边框为1像素都不会弄。
莫相离 该用户已被删除
沙发
发表于 2015-2-4 09:39:41 | 只看该作者
在学习的过程中不能怕麻烦,不能有懒惰的思想。学习php首先应该搭建一个lamp环境或者是wamp环境。这是学习php开发的根本。虽然网络上有很多集成的环境,安装很方便,使用起来也很稳定、
只想知道 该用户已被删除
板凳
发表于 2015-2-6 21:23:51 | 只看该作者
不禁又想起那些说php是草根语言的人,为什么认得差距这么大呢。
再现理想 该用户已被删除
地板
发表于 2015-2-9 02:09:28 | 只看该作者
我学习了一段时间后,我发现效果并不好(估计是我自身的问题)。因为一个人的精力总是有限的,同时学习这么多,会导致每个的学习时间都得不到保证。
海妖 该用户已被删除
5#
发表于 2015-2-9 19:55:48 | 只看该作者
如果你已经到这种程度了,那么你已经可以做我的老师了。其实php也分很多的区域,
灵魂腐蚀 该用户已被删除
6#
发表于 2015-2-12 08:49:15 | 只看该作者
说php的话,首先得提一下数组,开始的时候我是最烦数组的,总是被弄的晕头转向,不过后来呢,我觉得数组里php里最强大的存储方法,所以建议新手们要学好数组。
老尸 该用户已被删除
7#
发表于 2015-3-3 00:24:59 | 只看该作者
在学习的过程中不能怕麻烦,不能有懒惰的思想。学习php首先应该搭建一个lamp环境或者是wamp环境。这是学习php开发的根本。虽然网络上有很多集成的环境,安装很方便,使用起来也很稳定、
8#
发表于 2015-3-8 00:17:25 | 只看该作者
学习php的目的往往是为了开发动态网站,phper就业的要求也涵盖了很多。我大致总结为:精通php和mysql
飘飘悠悠 该用户已被删除
9#
发表于 2015-3-8 21:10:12 | 只看该作者
php里的数组为空的时候是不能拿来遍历的;(这个有点低级啊,不过我刚被这个边界问题墨迹了好长一会)
分手快乐 该用户已被删除
10#
发表于 2015-3-16 04:51:21 | 只看该作者
遇到出错的时候,我经常把错误信息直接复制到 google的搜索栏,一般情况都是能搜到结果的,不过有时候会搜出来一大片英文的出来,这时候就得过滤一下,吧中文的弄出来,挨着式方法。
飘灵儿 该用户已被删除
11#
发表于 2015-3-17 09:39:42 | 只看该作者
建议加几个专业的phper的群,当然啦需要说话的人多,一处一点问题能有人回答你的,当然啦要让人回答你的问题,平时就得躲在里面聊天,大家混熟啦,愿意回答你问题的人自然就多啦。
爱飞 该用户已被删除
12#
发表于 2015-3-24 05:57:10 | 只看该作者
首推的搜索引擎当然是Google大神,其次我比较喜欢 百度知道。不过搜出来的结果往往都是 大家copy来copy去的,运气的的概率很大。
柔情似水 该用户已被删除
13#
发表于 2015-3-28 17:39:07 | 只看该作者
最后介绍一个代码出错,但是老找不到错误方法,就是 go to wc (囧),出去换换气没准回来就找到错误啦。
兰色精灵 该用户已被删除
14#
发表于 2015-3-30 15:49:28 | 只看该作者
小鸟是第一次发帖(我习惯潜水的(*^__^*) 嘻嘻……),有错误之处还请大家批评指正,另外,前些日子听人说有高手能用php写驱动程序,真是学无止境,人外有人,天外有天。
冷月葬花魂 该用户已被删除
15#
发表于 2015-4-4 12:27:42 | 只看该作者
其实也不算什么什么心得,在各位大侠算是小巫见大巫了吧,望大家不要见笑,若其中有错误的地方请各位大虾斧正。
若天明 该用户已被删除
16#
发表于 2015-4-6 09:02:59 | 只看该作者
我要在声明一下:我是个菜鸟!!我对php这门优秀的语言也是知之甚少。但是我要在这里说一下php在网站开发中最常用的几个功能:
愤怒的大鸟 该用户已被删除
17#
发表于 2015-4-12 03:36:37 | 只看该作者
因为blog这样的可以让你接触更多要学的知识,可以接触用到类,模板,js ,ajax
admin 该用户已被删除
18#
发表于 2015-4-14 23:16:30 | 只看该作者
当留言板完成的时候,下步可以把做1个单人的blog程序,做为目标,
精灵巫婆 该用户已被删除
19#
发表于 2015-4-16 17:51:45 | 只看该作者
曾经犯过一个很低级的错误,我在文件命名的时候用了一个横线\\\\\\\'-\\\\\\\' 号,结果找了好几个小时的错误,事实是命名的时候 是不能用横线 \\\\\\\'-\\\\\\\' 的,应该用的是下划线  \\\\\\\'_\\\\\\\' ;
不帅 该用户已被删除
20#
发表于 2015-4-22 08:01:57 | 只看该作者
写的比较杂,因为我也是个新手,不当至于大家多多指正。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|仓酷云 鄂ICP备14007578号-2

GMT+8, 2024-12-24 07:38

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表