[PHP] 织梦分词算法源代码 →→→→→进入此内容的聊天室

来自 , 2019-09-22, 写在 PHP, 查看 106 次.
URL http://www.code666.cn/view/ae587cfe
  1. <?
  2. /*******************************
  3. //织梦分词算法 www.dedecms.com
  4. //作者:IT柏拉图 QQ:2500875
  5. //本程式公提供测试用,不包含岐义处理和词语标注的功能
  6. //不过默认的情况是有载入标注词典的,有需要可自行加入
  7. //本程式词典由织梦组织整理,严禁用于商业用途
  8. ********************************/
  9. class SplitWord
  10. {
  11.         var $TagDic = Array();
  12.         var $RankDic = Array();
  13.         var $OneNameDic = Array();
  14.         var $TwoNameDic = Array();
  15.         var $SourceString = '';
  16.         var $ResultString = '';
  17.         var $SplitChar = ' '; //分隔符
  18.         var $SplitLen = 4; //保留词长度
  19.         var $EspecialChar = "和|的|是";
  20.         var $NewWordLimit = "在|的|与|或|就|你|我|他|她|有|了|是|其|能|对|地";
  21.        
  22.         //这里可以按需要加入常用的量词,
  23.         //程序会检测词语第一个字是否为这些词和上一个词是否为数词,然后结合为单词
  24.         var $CommonUnit = "年|月|日|时|分|秒|点|元|百|千|万|亿|位|辆";
  25.        
  26.         var $CnNumber = "%|+|-|0|1|2|3|4|5|6|7|8|9|.";
  27.         var $CnSgNum = "一|二|三|四|五|六|七|八|九|十|百|千|万|亿|数";
  28.         var $MaxLen = 13; //词典最大 7 中文字,这里的数值为字节数组的最大索引
  29.         var $MinLen = 3;  //最小 2 中文字,这里的数值为字节数组的最大索引
  30.         var $CnTwoName = "端木 南宫 谯笪 轩辕 令狐 钟离 闾丘 长孙 鲜于 宇文 司徒 司空 上官 欧阳 公孙 西门 东门 左丘 东郭 呼延 慕容 司马 夏侯 诸葛 东方 赫连 皇甫 尉迟 申屠";
  31.         var $CnOneName = "赵钱孙李周吴郑王冯陈褚卫蒋沈韩杨朱秦尤许何吕施张孔曹严华金魏陶姜戚谢邹喻柏水窦章云苏潘葛奚范彭郎鲁韦昌马苗凤花方俞任袁柳酆鲍史唐费廉岑薛雷贺倪汤滕殷罗毕郝邬安常乐于时傅皮卡齐康伍余元卜顾孟平黄穆萧尹姚邵堪汪祁毛禹狄米贝明臧计伏成戴谈宋茅庞熊纪舒屈项祝董粱杜阮蓝闵席季麻强贾路娄危江童颜郭梅盛林刁钟徐邱骆高夏蔡田樊胡凌霍虞万支柯咎管卢莫经房裘缪干解应宗宣丁贲邓郁单杭洪包诸左石崔吉钮龚程嵇邢滑裴陆荣翁荀羊於惠甄魏加封芮羿储靳汲邴糜松井段富巫乌焦巴弓牧隗谷车侯宓蓬全郗班仰秋仲伊宫宁仇栾暴甘钭厉戎祖武符刘姜詹束龙叶幸司韶郜黎蓟薄印宿白怀蒲台从鄂索咸籍赖卓蔺屠蒙池乔阴郁胥能苍双闻莘党翟谭贡劳逄姬申扶堵冉宰郦雍郤璩桑桂濮牛寿通边扈燕冀郏浦尚农温别庄晏柴翟阎充慕连茹习宦艾鱼容向古易慎戈廖庚终暨居衡步都耿满弘匡国文寇广禄阙东殴殳沃利蔚越夔隆师巩厍聂晁勾敖融冷訾辛阚那简饶空曾沙须丰巢关蒯相查后江游竺";
  32.  
  33.   //------------------------------
  34.   //php4构造函数
  35.   //------------------------------
  36.   function SplitWord(){
  37.         $this->__construct();
  38.   }
  39.   //------------------------------
  40.   //php5构造函数
  41.   //------------------------------
  42.   function __construct(){              
  43.         //载入姓氏词典
  44.         for($i=0;$i<strlen($this->CnOneName);$i++)
  45.         {
  46.                 $this->OneNameDic[$this->CnOneName[$i].$this->CnOneName[$i+1]] = 1;
  47.                 $i++;
  48.         }
  49.         $twoname = explode(" ",$this->CnTwoName);
  50.         foreach($twoname as $n){ $this->TwoNameDic[$n] = 1; }
  51.         unset($twoname);
  52.         unset($this->CnOneName);
  53.         unset($this->CnTwoName);
  54.        
  55.         //高级分词,预先载入词典以提分词高速度
  56.         $dicfile = dirname(__FILE__)."/dededic.csv";
  57.         $fp = fopen($dicfile,'r');
  58.         while($line = fgets($fp,256)){
  59.                   $ws = explode(' ',$line);
  60.                   $this->TagDic[$ws[0]] = $ws[1];
  61.                   $this->RankDic[strlen($ws[0])][$ws[0]] = $ws[2];
  62.         }
  63.         fclose($fp);
  64.   }
  65.  
  66.   //--------------------------
  67.   //析放资源
  68.   //--------------------------
  69.   function Clear()
  70.   {
  71.         @fclose($this->QuickDic);
  72.   }
  73.  
  74.   //----------------------------
  75.   //设置源字符串
  76.   //----------------------------
  77.   function SetSource($str){
  78.         $this->SourceString = trim($this->ReviseString($str));
  79.         $this->ResultString = "";
  80.   }
  81.  
  82.   //-----------------------------
  83.   //检查字符串是否不存在中文
  84.   //-----------------------------
  85.   function NotGBK($str)
  86.   {
  87.     if($str=="") return "";
  88.     //因为粗分的时候已经处理,因此不必要检查所的字符
  89.         if( ord($str[0])>0x80 ) return false;
  90.         else return true;
  91.   }
  92.   //-----------------------------
  93.   //RMM分词算法
  94.   //-----------------------------
  95.   function SplitRMM($str=""){
  96.         if($str!="") $this->SetSource(trim($str));
  97.         if($this->SourceString=="") return "";
  98.         //对文本进行粗分
  99.         $this->SourceString = $this->ReviseString($this->SourceString);
  100.         //对特定文本进行分离
  101.         $spwords = explode(" ",$this->SourceString);
  102.         $spLen = count($spwords);
  103.         $spc = $this->SplitChar;
  104.         for($i=($spLen-1);$i>=0;$i--){
  105.                 if(trim($spwords[$i])=="") continue;
  106.                 if($this->NotGBK($spwords[$i])){
  107.                         if(ereg("[^0-9\.\+\-]",$spwords[$i]))
  108.                         { $this->ResultString = $spwords[$i].$spc.$this->ResultString; }
  109.                         else
  110.                         {
  111.                                 $nextword = "";
  112.                                 @$nextword = substr($this->ResultString,0,strpos($this->ResultString," "));
  113.                                 if(ereg("^".$this->CommonUnit,$nextword)){
  114.                                         $this->ResultString = $spwords[$i].$this->ResultString;
  115.                                 }else{
  116.                                         $this->ResultString = $spwords[$i].$spc.$this->ResultString;
  117.                                 }
  118.                         }
  119.                 }
  120.                 else
  121.                 {
  122.                   $c = $spwords[$i][0].$spwords[$i][1];
  123.                   $n = hexdec(bin2hex($c));
  124.                   if($c=="《") //书名
  125.                   { $this->ResultString = $spwords[$i].$spc.$this->ResultString; }
  126.                   else if($n>0xA13F && $n < 0xAA40) //标点符号
  127.                   { $this->ResultString = $spwords[$i].$spc.$this->ResultString; }
  128.                   else //正常短句
  129.                   {
  130.                         if(strlen($spwords[$i]) <= $this->SplitLen)
  131.                         {
  132.                                 //如果结束符为特殊分割词,分离处理
  133.                                 if(ereg($this->EspecialChar."$",$spwords[$i],$regs)){
  134.                                                 $spwords[$i] = ereg_replace($regs[0]."$","",$spwords[$i]).$spc.$regs[0];
  135.                                 }
  136.                                 //是否为常用单位
  137.                                 if(!ereg("^".$this->CommonUnit,$spwords[$i]) || $i==0){
  138.                                         $this->ResultString = $spwords[$i].$spc.$this->ResultString;
  139.                                 }else{
  140.                                         $this->ResultString = $spwords[$i-1].$spwords[$i].$spc.$this->ResultString;
  141.                                         $i--;
  142.                                 }
  143.                         }
  144.                         else
  145.                         {
  146.                                 $this->ResultString = $this->RunRMM($spwords[$i]).$spc.$this->ResultString;
  147.                         }
  148.                   }
  149.           }
  150.         }
  151.         return $this->ResultString;
  152.   }
  153.   //对全中文字符串进行逆向匹配方式分解
  154.   function RunRMM($str)
  155.   {
  156.         $spc = $this->SplitChar;
  157.         $spLen = strlen($str);
  158.         $rsStr = "";
  159.         $okWord = "";
  160.         $tmpWord = "";
  161.         $WordArray = Array();
  162.         //逆向字典匹配
  163.         for($i=($spLen-1);$i>=0;)
  164.         {
  165.                 //当i达到最小可能词的时候
  166.                 if($i<=$this->MinLen){
  167.                         if($i==1){
  168.                           $WordArray[] = substr($str,0,2);
  169.                           //echo "Min 1: ".substr($str,0,2)."<br>";
  170.                   }else
  171.                         {
  172.                            $w = substr($str,0,$this->MinLen+1);
  173.                            if($this->IsWord($w)){
  174.                                 $WordArray[] = $w;                                                                                  
  175.                            }else{
  176.                                    $WordArray[] = substr($str,2,2);
  177.                                    $WordArray[] = substr($str,0,2);
  178.                                    //echo "Min 2-2: ".substr($str,0,2).substr($str,2,2)."<br>";
  179.                            }
  180.                   }
  181.                         $i = -1; break;
  182.                 }
  183.                 //分析在最小词以上时的情况
  184.                 if($i>=$this->MaxLen) $maxPos = $this->MaxLen;
  185.                 else $maxPos = $i;
  186.                 $isMatch = false;
  187.                 for($j=$maxPos;$j>=0;$j=$j-2){
  188.                          $w = substr($str,$i-$j,$j+1);
  189.                          if($this->IsWord($w)){
  190.                                 $WordArray[] = $w;
  191.                                 //echo "EG: ".$w." $str $i $j<br>";
  192.                                 $i = $i-$j-1;
  193.                                 $isMatch = true;
  194.                                 break;
  195.                          }
  196.                 }
  197.                 if(!$isMatch){
  198.                         if($i>1) {
  199.                                 $WordArray[] = $str[$i-1].$str[$i];
  200.                                 //echo "NOT EG: ".$w."<br>";
  201.                                 $i = $i-2;
  202.                         }
  203.                 }
  204.         }//End For
  205.         $rsStr = $this->ParOther($WordArray);
  206.         return $rsStr;
  207.   }
  208.   //
  209.   //进行名字识别和其它数词识别
  210.   //
  211.   function ParOther($WordArray)
  212.   {
  213.         $wlen = count($WordArray)-1;
  214.         $rsStr = "";
  215.         $spc = $this->SplitChar;
  216.         for($i=$wlen;$i>=0;$i--)
  217.         {
  218.  
  219.                 //数量词
  220.                 if(ereg($this->CnSgNum,$WordArray[$i])){
  221.                         $rsStr .= $spc.$WordArray[$i];
  222.                         if($i>0 && ereg("^".$this->CommonUnit,$WordArray[$i-1]))
  223.                         { $rsStr .= $WordArray[$i-1]; $i--; }
  224.                         else{
  225.                                 while($i>0 && ereg($this->CnSgNum,$WordArray[$i-1]))
  226.                                 { $rsStr .= $WordArray[$i-1]; $i--; }
  227.                         }
  228.                         continue;
  229.                 }
  230.                 //双字姓
  231.                 if(strlen($WordArray[$i])==4 && isset($this->TwoNameDic[$WordArray[$i]]))
  232.                 {
  233.                         $rsStr .= $spc.$WordArray[$i];
  234.                         if($i>0&&strlen($WordArray[$i-1])==2){
  235.                                 $rsStr .= $WordArray[$i-1];$i--;
  236.                                 if($i>0&&strlen($WordArray[$i-1])==2){ $rsStr .= $WordArray[$i-1];$i--; }
  237.                         }
  238.                 }
  239.                 //单字姓
  240.                 else if(strlen($WordArray[$i])==2 && isset($this->OneNameDic[$WordArray[$i]]))
  241.                 {
  242.                         $rsStr .= $spc.$WordArray[$i];
  243.                         if($i>0&&strlen($WordArray[$i-1])==2){
  244.                                  $rsStr .= $WordArray[$i-1];$i--;
  245.                                  if($i>0 && strlen($WordArray[$i-1])==2){ $rsStr .= $WordArray[$i-1];$i--; }
  246.                         }
  247.                 }
  248.                 //普通词汇
  249.                 else{
  250.                         $rsStr .= $spc.$WordArray[$i];
  251.                 }
  252.         }
  253.         //返回本段分词结果
  254.         $rsStr = preg_replace("/^".$spc."/","",$rsStr);
  255.         return $rsStr;
  256.   }
  257.   //---------------------------------
  258.   //判断词典里是否存在某个词
  259.   //---------------------------------
  260.   function IsWord($okWord){
  261.         $slen = strlen($okWord);
  262.         if($slen > $this->MaxLen) return false;
  263.         else return isset($this->RankDic[$slen][$okWord]);
  264.   }
  265.   //------------------------------
  266.   //整理字符串(对标点符号,中英文混排等初步处理)
  267.   //------------------------------
  268.   function ReviseString($str)
  269.   {
  270.         $spc = $this->SplitChar;
  271.     $slen = strlen($str);
  272.     if($slen==0) return '';
  273.     $okstr = '';
  274.     $prechar = 0; // 0-空白 1-英文 2-中文 3-符号
  275.     for($i=0;$i<$slen;$i++){
  276.       if(ord($str[$i]) < 0x81)
  277.       {
  278.         //英文的空白符号
  279.         if(ord($str[$i]) < 33){
  280.           if($prechar!=0&&$str[$i]!="\r"&&$str[$i]!="\n") $okstr .= $spc;
  281.           $prechar=0;
  282.           continue;
  283.         }else if(ereg("[^0-9a-zA-Z@\.%#:/\\&_-]",$str[$i]))
  284.         {
  285.           if($prechar==0)
  286.           {     $okstr .= $str[$i]; $prechar=3;}
  287.           else
  288.           { $okstr .= $spc.$str[$i]; $prechar=3;}
  289.         }else
  290.         {
  291.                 if($prechar==2||$prechar==3)
  292.                 { $okstr .= $spc.$str[$i]; $prechar=1;}
  293.                 else
  294.                 {
  295.                   if(ereg("@#%:",$str[$i])){ $okstr .= $str[$i]; $prechar=3; }
  296.                   else { $okstr .= $str[$i]; $prechar=1; }
  297.                 }
  298.         }
  299.       }
  300.       else{
  301.         //如果上一个字符为非中文和非空格,则加一个空格
  302.         if($prechar!=0 && $prechar!=2) $okstr .= $spc;
  303.         //如果中文字符
  304.         if(isset($str[$i+1])){
  305.           $c = $str[$i].$str[$i+1];
  306.          
  307.           if(ereg($this->CnNumber,$c))
  308.           { $okstr .= $this->GetAlabNum($c); $prechar = 2; $i++; continue; }
  309.          
  310.           $n = hexdec(bin2hex($c));
  311.           if($n>0xA13F && $n < 0xAA40)
  312.           {
  313.             if($c=="《"){
  314.                 if($prechar!=0) $okstr .= $spc." 《";
  315.                 else $okstr .= " 《";
  316.                 $prechar = 2;
  317.             }
  318.             else if($c=="》"){
  319.                 $okstr .= "》 ";
  320.                 $prechar = 3;
  321.             }
  322.             else{
  323.                 if($prechar!=0) $okstr .= $spc.$c;
  324.                 else $okstr .= $c;
  325.                 $prechar = 3;
  326.             }
  327.           }
  328.           else{
  329.             $okstr .= $c;
  330.             $prechar = 2;
  331.           }
  332.           $i++;
  333.         }
  334.       }//中文字符
  335.     }//结束循环
  336.     return $okstr;
  337.   }
  338.   //-----------------------------------------
  339.         //尝试识别新词,字符串参数为已经分词处理的串
  340.         //----------------------------------------
  341.   function FindNewWord($spwords,$maxlen=6)
  342.   {
  343.     $okstr = '';
  344.     $ws = explode(' ',$spwords);
  345.     $newword = '';
  346.     $nws = '';
  347.     foreach($ws as $w)
  348.     {
  349.       $w = trim($w);
  350.       if(strlen($w)==2 && !preg_match("/[0-9a-zA-Z]/",$w) && !preg_match("/".$this->NewWordLimit."/",$w) )
  351.       { $newword .= " ".$w;}
  352.       else
  353.       {
  354.         if($newword!="")
  355.         {
  356.           $nw = str_replace(' ','',$newword);
  357.           if(strlen($nw)>2)
  358.           {
  359.             if(strlen($nw) <= $maxlen){ $okstr .= ' '.$nw; $nws[$nw] = 0; }
  360.             else $okstr .= ' '.$newword;
  361.           }
  362.           else
  363.           { $okstr .= ' '.$newword; }
  364.           $newword = '';
  365.         }
  366.         $okstr .= ' '.$w;
  367.       }
  368.     }
  369.     if($newword!="") $okstr .= $newword;
  370.     $okstr = preg_replace("/ {1,}/"," ",$okstr);
  371.     if(is_array($nws))
  372.     {
  373.       $this->m_nws = $nws;
  374.       foreach($nws as $k=>$w)
  375.       {
  376.         $w = "";
  377.         for($i=0;$i<strlen($k);$i++){
  378.           if( ord($k[$i]) > 0x80 ){
  379.             $w .= " ".$k[$i];
  380.             if(isset($k[$i+1])){ $w .= $k[$i+1]; $i++;}
  381.           }
  382.           else
  383.             $w .= " ".$k[$i];
  384.           $w .= " ";
  385.         }
  386.         $w = preg_replace("/ {1,}/"," ",$w);
  387.         $okstr = str_replace($w," ".$k." ",$okstr);
  388.         $okstr = str_replace($k." "," ".$k." ",$okstr);
  389.         $okstr = str_replace(" ".$k," ".$k." ",$okstr);
  390.       }
  391.     }
  392.     return $okstr;
  393.   }
  394.   //----------------------------------------------
  395.   //除去字串中的重复词,生成索引字符串,字符串参数为已经分词处理的串
  396.   //--------------------------------------------------
  397.   function GetIndexText($okstr,$ilen=-1)
  398.   {
  399.     if($okstr=="") return "";
  400.     $ws = explode(" ",$okstr);
  401.     $okstr = "";
  402.     $wks = "";
  403.     foreach($ws as $w)
  404.     {
  405.       $w = trim($w);
  406.       //排除小于2的字符
  407.       if(strlen($w)<2) continue;
  408.       //排除数字或日期
  409.       if(!ereg("[^0-9:-]",$w)) continue;
  410.       if(strlen($w)==2&&ord($w[0])>0x80) continue;
  411.       if(isset($wks[$w])) $wks[$w]++;
  412.       else $wks[$w] = 1;
  413.     }
  414.     if(is_array($wks))
  415.     {
  416.       arsort($wks);
  417.       if($ilen==-1)
  418.       { foreach($wks as $w=>$v) $okstr .= $w." "; }
  419.       else
  420.       {
  421.         foreach($wks as $w=>$v){
  422.           if((strlen($okstr)+strlen($w)+1)<$ilen) $okstr .= $w." ";
  423.           else break;
  424.         }
  425.       }
  426.     }
  427.     return trim($okstr);
  428.   }
  429.   //
  430.   //把全角数字转为半角数字
  431.   //
  432.   function GetAlabNum($fnum)
  433.   {
  434.           $nums = array("0","1","2","3","4","5","6","7","8","9","+","-","%",".");
  435.           $fnums = "0123456789+-%.";
  436.           for($i=0;$i<count($nums);$i++){
  437.                 if($nums[$i]==$fnum) return $fnums[$i];
  438.           }
  439.           return $fnum;
  440.   }
  441. }//End Class
  442. ?>

回复 "织梦分词算法源代码"

这儿你可以回复上面这条便签

captcha