zoukankan      html  css  js  c++  java
  • Lucene.net 搜索引擎中中文词组分词的实现

     Lucene.net标准分词器在英文分词中有非常好的体验。比喻说:在邮件,IP地址,符号处理方面,它都处理得非常好。只是很遗憾,它不支持中文词组分词。于是,我就通过修改里面的核心代码让它扩展,支持中文的分词。

    目标:使它能够增加对中文词组的切词。

    效果:

    原句:“我是中国人!I am chiness!Email:youpeizun126@126.com;IP:172.17.34.168”

    切词效果:

    我/是/中国人/中国/中/国/人/Email/youpeizun126@126.com/IP/172.17.34.168

    所要完成的任务:

    1. 装载词库

    2. 截取一段连续的中文字段

    3. 进行连续的分词.



    下面是设计扩展Lucene.net标准分词器的支持中文词组分词的流程图.



           接下来,我把扩展Lucene.net标准分词器所写的核心代码,主要包含三个函数,它们分别实现装载词典,载取连续中文字段,中文词组分词算法功能.

    中文词组分词核心代码
    #region 加载中文词典
            public  void LoadDirectory(string path)
            {
                if(!File.Exists("words.txt"))
                    return;
               TextReader tr_words=new StreamReader("words.txt",System.Text.Encoding.Default);
                System.Diagnostics.Debug.Write("begin read words");
                    if(directory==null)
              { 
                        directory=new System.Collections.Hashtable();
                        try
                        {
                            string word=null;
                            while((word=tr_words.ReadLine())!=null)
                            {
                                try
                                {
    
                                    if(directory[word]==null)
                                    {
                                    directory.Add(word,word);
                                    
                                    }
    
                                }
                                catch(SystemException ex_)
                                {
                                
                                }
                            }
                        }
                        catch(SystemException ex)
                        {
                        
                        }
              }
    #endregion
               
            }
    #region 截取一段连续中文字段
            private void  InitChinessText()
            {
                
                textlengh=0;        
                cn_index=0;
                chinesstext[0]=token.image;
                textlengh++;
                cn_start=token.beginColumn;
                isCnToken=true; 
                bool isCN= true;
                
                while(isCN&&textlengh<255)
                {   token=token_source.GetNextToken();
                    if(token.kind!=0)
                    {
                        isCN=Char.GetUnicodeCategory(token.image,0).Equals(System.Globalization.UnicodeCategory.OtherLetter);
                    }
                    else
                        isCN=false;
                    if(isCN)
                    {     
                        
                            
                        
                            chinesstext[textlengh]=token.image;        
                            textlengh++;
                    }
                    else
                    {
                        
                        cn_end_token=token;
                    }
                
                }
                if(textlengh>=4)
                {
                wordlengh=4;
                }
                else
                    wordlengh=textlengh;
            
                
            
            }
    #endregion
    #region 实现中文分词算法
            private string GetNextTokenText()
            {   string text=null;
             
                if(wordlengh==4)
                {
                    text=chinesstext[cn_index]+chinesstext[cn_index+1]+chinesstext[cn_index+2]+chinesstext[cn_index+3];
                    if(directory[text]!=null)
                    {
                        
                    }
                    wordlengh--;
                }
                if(wordlengh==3)
                {
                    text=chinesstext[cn_index]+chinesstext[cn_index+1]+chinesstext[cn_index+2];
                    wordlengh--;
                    if(directory[text]!=null)
                    {
                    goto return_;
                    }
                    
                }
                if(wordlengh==2)
                {
                    text=chinesstext[cn_index]+chinesstext[cn_index+1];
                    wordlengh--;
                    if(directory[text]!=null)
                    {
                        goto return_;
                    }
                
                }
                if(wordlengh==1)
                {
                    text=chinesstext[cn_index];
                    cn_index++;
                    if((textlengh-cn_index)>=4)
                    {
                        wordlengh=4;
                    }
                    else
                        if((textlengh-cn_index)==0)
                    {
                        isCnToken=false;
                        jj_ntk=cn_end_token.kind;
                        token=new Token();
                        token.next=cn_end_token;
                    
                    }
                    else
                    {
                    wordlengh=textlengh-cn_index;
                    }
    
                
                }
                return_:
                    return text;
            }
            
    #endregion
            */
    

      


            

     

    一、中文分词方式:

    中文分词几种常用的方式:

    A. 单字分词

    单字分词,顾名思义,就是按照中文一个字一个字地进行分词。如:我们是中国人,效果:我\们\是\中\国\人。

    B. 二分法

    二分法,就是按两个字进行切分。如:我们是中国人,效果:我们\们是\是中\中国\国人。

    C. 词库分词

    词库分词,就是按某种算法构造词然后去匹配已建好的词库集合,如果匹配到就切分出来成为词语。通常词库分词被认为是最理想的中文分词算法如:我们是中国人,通成效果为:我们\是\中国\中国人。

    二、Lucene.net中五种中文分词效果探究

    在Lucene.net中有很多种分词器,不同分词器使用了不同的分词算法,有不同的分词效果,满足不同的需求!在这里主要是看看其中五中分词器用来对中文切词的效果。五中分词器分别为:StandardTokenizer,CJKTokenizer,ChinessTokenizer,LowerCaseTokenizer,WhitespaceTokenizer;

       下面就来测试一下它们切词的效果:

       测试目标:是否支持中文词语,英文单词,邮件,IP地址,标点符号,数字,数学表达式的切割。
       测试文字:“我们是中国人; 我们 是 人;we are chiness; 172.16.34.172;youpeizun@126.com;#$*;85*34;58 69”

    测试StandardTokenizer的分词情况如下:

    我/ 们/ 是/ 中/ 国/ 人/ 我/ 们/ 是/ 人/ we/ are/ chiness/ 172.16.34.172/ youpeizun@126.com/ 85/ 34/ 58/ 69/

    测试CJKTokenizer的分词情况如下:

    我们/ 们是/ 是中/ 中国/ 国人/ 我们/ 是/ 人/ we/ chiness/ 172/ 16/ 34/ 172/ youpe

    izun/ 126/ com/ #/ 85/ 34/ 58/ 69/

    测试ChinessTokenizer的分词情况如下:

    我/ 们/ 是/ 中/ 国/ 人/ 我/ 们/ 是/ 人/ we/ are/ chiness/ 172/ 16/ 34/ 172/ youp

    eizun/ 126/ com/ 85/ 34/ 58/ 69/

    测试LowerCaseTokenizer的分词情况如下:

    我们是中国人/我们/是/人/we/are/chiness/youpeizun/com/

    测试WhitespaceTokenizer的分词情况如下:

    我们是中国人;/我们/是/人;we/are/chiness;/172.16.34.172;youpeizun@126.com;#$*;85*

    34;58/69/

    测试代码: 


    using System;
    using System.Collections.Generic;
    using System.Text;
    using Lucene.Net.Analysis.Standard;
    using Lucene.Net.Analysis;
    using Lucene.Net.Index;
    using Lucene.Net.Documents;
    using System.IO;
    using Lucene.Net.Analysis.Cn;
    using Lucene.Net.Analysis.CJK;
    //date:11-02-2007
    //home page:http://www.cnblogs.com/xuanfeng
    //author:peizunyou
    namespace TokenizerTest
    {
        class TokenizerTest
        {
            static void Main(string[] args)
            {
                string testText = "我们是中国人; 我们 是 人;we are chiness; 172.16.34.172;youpeizun@126.com;#$*;85*34;58 69";
                Console.WriteLine("测试文字:"+testText);
                Console.WriteLine("测试StandardTokenizer的分词情况如下:");
                TestStandardTokenizer(testText);
                Console.WriteLine("测试CJKTokenizer的分词情况如下:");
                TestCJKTokenizer(testText);
                Console.WriteLine("测试ChinessTokenizer的分词情况如下:");
                TestChinessTokenizer(testText);
                Console.WriteLine("测试LowerCaseTokenizer的分词情况如下:");
                TestLowerCaseTokenizer(testText);
                Console.WriteLine("测试WhitespaceTokenizer的分词情况如下:");
                TestWhitespaceTokenizer(testText);
                Console.Read();
            }

            static  void TestStandardTokenizer(string text)
            {
                TextReader tr = new StringReader(text);

                StandardTokenizer st = new StandardTokenizer(tr);
             
                while (st.Next() != null)
                {

                    Console.Write(st.token.ToString()+"/ ");
                }

                Console.WriteLine();
            }

            static void TestCJKTokenizer(string text)
            {
                TextReader tr = new StringReader(text);
                int end = 0;
                CJKAnalyzer cjkA = new CJKAnalyzer();
                TokenStream ts = cjkA.TokenStream(tr);
                while(end<text.Length)
                {
                    Lucene.Net.Analysis.Token t = ts.Next();
                    end = t.EndOffset();
                    Console.Write(t.TermText()+"/ ");
                }

                Console.WriteLine();
            }

            static void TestChinessTokenizer(string text)
            {
                TextReader tr = new StringReader(text);
                ChineseTokenizer ct = new ChineseTokenizer(tr);
                int end = 0;
                Lucene.Net.Analysis.Token t;
                while(end<text.Length)
                {
                    t = ct.Next();
                    end = t.EndOffset();
                    Console.Write(t.TermText()+"/ ");
                }

                Console.WriteLine();
            
            }

            
            static void TestLowerCaseTokenizer(string text)
            {
                TextReader tr = new StringReader(text);
                SimpleAnalyzer sA = new SimpleAnalyzer();
                //SimpleAnalyzer使用了LowerCaseTokenizer分词器
                TokenStream ts = sA.TokenStream(tr);
                Lucene.Net.Analysis.Token t;
                while((t=ts.Next())!=null)
                {
                    Console.Write(t.TermText()+"/");
                }

                Console.WriteLine();
            }

            static void TestWhitespaceTokenizer(string text)
            {
                TextReader tr = new StringReader(text);
       
                WhitespaceAnalyzer sA = new WhitespaceAnalyzer();
                TokenStream ts = sA.TokenStream(tr);
                Lucene.Net.Analysis.Token t;
                while ((t = ts.Next()) != null)
                {
                    Console.Write(t.TermText() + "/");
                }

                Console.WriteLine();
            }

        }

    }

    测试代代码下载

    三、            五中分词器代码设计探究

           从下面分词器代码设计中的静态结构图可以清晰的看出其继承关系。无论是哪个分词器,其分词最终实现的算法都是在Next()方法,想深入了解,请看其相关源码。

  • 相关阅读:
    架构漫谈1
    如何将本地工程上传到github
    寒假日报day23
    寒假日报----首都之窗爬虫大作业
    寒假日报day22
    寒假日报day21
    关于webmagic的post请求
    寒假日报day20
    寒假日报day19
    吾日三省吾身(41)
  • 原文地址:https://www.cnblogs.com/haiyabtx/p/2770126.html
Copyright © 2011-2022 走看看