Lucene学习笔记(五)

来源:互联网 发布:手机外汇看盘软件 编辑:程序博客网 时间:2024/06/05 08:27

 五、Lucene的分析器:
                                大纲:1. Lucene分析器Analyzer
                                         2. JavaCC与Tokenizer
                                         3. Lucene内建的分析器
                                         4. 定制自己的分词器和过滤器
          
  1. Lucene分析器----Analyzer
  
   1.1 Analyzer的概述:中文翻译是"分析器"。
   它主要用于分析切词或者过滤搜索时遇到的各种文本。用更具体的话说其实就是"分词"和"过滤"。
   
   ** 作用:分析器作用与索引和文本之间,索引要进入索引库的文本资源都要先通过分析器的分析(切词和过滤),
   以便确保索引库中的索引内容的正确性。
   如果不经过分析器分析就把文本内容存入索引中,那么会引起索引内容数据的不一致,也会降低索引的效率,
   进而影响整个搜索引擎的性能。
   
   ** 注意:一个分析器主要包括分词器和过滤器两种组件。
   
    1.1.1 分词器:就是用于对文本资源进行切词,
    将文本按照规则切分为一个个可以进入索引的最小单位--分词。(具体下边标题会讲到)
    
    1.1.2 过滤器:对分词器切成的索引最小单位,即对分词进行预处理操作。
    比如大写转小写、复数转单数、根据语义改写拼写错误的单词等。(具体下边标题会讲到)
   
   ** 说明:此处我们着重讲解Analyzer分析器的实现。所有的分析器都是继承的org.apache.lucene.analysis.Analyzer抽象类,
   该类中只定义了两个方法《自己可查看源代码》
   
   1.2 分词器(Tokenizer)和过滤器(TokenFilter):前边说过分析器的功能就是进行分词和过滤。
   所以在Analyzer分析器的对象中一定会保存对分词器对像和过滤器对象的引用,然后调用分词器对象和过滤器对象的API接口,
   以便完成切词和过滤的功能目的。
   
   ** 说明:在Lucene中,分词器和过滤器分别是通过继承抽象类Tokenizer和TokenFilter来实现的。
   《可以自己查看Tokenizer和TokenFilter类的源代码》
   
   ** 注意:从分析器的功能角度来看,其本身并不能做任何事,因为它完全依赖与分词器和过滤器完成目的任务。
   一个分析器的所有工作就是将分词器和过滤器进行合理的组合,利用它们对文本完成分词和过滤的效果。
          
   1.3 使用StandardAnalyzer进行测试:上一小节中了解到,要使用Lucene的分析机制,就要扩展Lucene的分析器基类。
   Lucene为我们提供了一个实现Analyzer的相当标准的分析器----StandardAnalyzer类。
   
   ** 说明:StandardAnalyzer可以理解成"标准分析器",它是最容易使用也是使用最频繁的一种Analyzer,
   它内部使用了Lucene自带的几种分词器和过滤器,来完成该标准分析器的功能效果。
   
   例子:
    //创建一个测试类
    public class StandardAnalyzerTest{
     //构造方法
     public StandardAnalyzerTest(){}
     
     //入口主方法
     public static void main(String[] args){
      //初始化一个StandardAnalyzer对象
      Analyzer analyzer = new StandardAnalyzer();
      //创建进行分析测试的字符串数据信息
      StringReader reader = new StringReader("People are always talking about 'the problem of youth'.");
      
      //创建一个TokenStream对象
      TokenStream tokenStream = analyzer.tokenStream(reader);
      //作为辅助输出时显示的行号
      int i = 0;
      //调用TokenStream类next()方法得到分析器分词过滤后的封装有分词信息的Token对象
      Token token = tokenSream.next();
      while(token != null){
       //增加i行号值
       i++;
       //Token.termText()是分析器分词过滤后的分词内容
       System.out.println("行数 " + i + " : " + token.termText());
       //取得下一个Token分词,然后再次输出
       token = tokenStream.next();
      }
     }
    }
   ** 结果:
     调用Analyzer.tokenStream(reader);意思就是将待分析的流对象,放入到分析器中进行一些准备信息,但是并没有分词和过滤,
     完成之后返回一个Lucene提供的TokenStream类的对象
     (** 注意:此时的TokenStream中保存的信息和传入的reader中保存的数据完全一样,就是说此时并没有完成分词和过滤)
     调用TokenStream类next()方法得到分析器分词过滤后的封装有分词信息的Token对象
     (** 注意:调用next方法的时候会对TokenStream中的要返回的数据
      (而不是所有的TokenStream中的数据都一次性的分词、过滤)
     进行分词和过滤),
     再调用Token.termText()就拿到了分词内容。
     打印结果:
       行1:people
       行2:always
       行3:talking
       行4:about
       行5:problem
       行6:youth
   
   ** StandardAnalyzer分析器的效果:
     
    1.3.1 对原有句子按照空格进行了分词(分词器干的)
    1.3.2 所有的大写字母都转换成了小写字母(过滤器干的)
    1.3.3 除掉了"are"、"of"、"the"等单词(过滤器干的)
    1.3.4 删除了所有的标点符号(过滤器干的)
          
   ** StandardAnalyzer分析器对中文的处理:将会把中文文本,一个字一个字的进行分词,也表明该分析器对中文的支持不太好。

    例如:代码不变,将输入文本改成:"我是中国人"
    
    打印结果:
      行1:我
      行2:是
      行3:中
      行4:国
      行5:人
    ** 说明:结果说明StandardAnalyzer分析器对于中文只能按照单字进行切词,除了切词还过滤掉了标点符号。
    如果实际中使用该分析器对中文创建索引,那么带来的缺点是使索引的内容过于庞大等,不便于建立索引和搜索效率的提高。
    《中文分析器后边会讲》
          
  2. JavaCC与Tokenizer :具体可以再次查看《Lucene指导书籍》,因为这部分没什么用处,完全可以不看。
   2.1 JavaCC简介:不重要。
   2.2 通过JavaCC构建的Lucene标准分析器原理:不重要。
   
  3. Lucene内建的分析器:Lucene提供了几种不同环境和需求下使用的Analyzer分析器,
  最常用的有StandardAnalyzer、SimpleAnalyzer和WhitespaceAnalyzer等。
  
   3.1 标准分析器----StandardAnalyzer类:在这我们说一下它的实现原理,看看它究竟是什么时机使用分词器分词和过滤器过滤的。
   
    3.1.1 由前边代码可知调用了StandardAnalyzer的tokenStream方法,看起源码后知道:
    将传入的Reader对象作为参数,
    分别使用了StandardTokenizer、StandardFilter、LowerCaseFilter、StopFilter的构造方法构造了对应的对象,
    但是构造方法中都什么都没座,也就是说"标准分析器"StandardAnalyzer的分词和过滤不是通过调用tokenStream方法实现的。
    
    3.1.2 测试中除了tokenStream方法外就是调用的TokenStream的next()方法,所以也可以猜到,分析器的分词和过滤就是通过该方法实现的。
    调用next时
    (这是针对我们上边的测试而言,如果是真正我们项目中用Lucene时,因为是创建索引和对搜索关键字处理时会用到分析器
    所以想看到调用tokenStream方法和next方法只能到创建索引和对搜索关键字进行处理的代码中找到
    如IndexWriter.addDocument()中调用DocumentWriter的方法中就能找到类似上边测试的代码),
    因为最后一个构造的是StopFilter类的对象,所以调用的是StopFilter类的next方法,跟踪源码发现,
    该next方法中有input.next(),其实input是作为参数传入的对象,弄明白后发现调用的是LowerCaseFilter的next方法,
    一次这样跟踪最后发现将会最先调用StandardTokenizer类的next方法,她是从Reader中取出下一个词,然后进行分词和过滤。
    
    例子:可以参看上边例子自己写一个简单的测试,这里就不列举了,因为比较简单。
    
   3.2 通用词分析器----StopAnalyzer类:
   该类的tokenStream方法return new StopFilter(new LowerCaseTokenizer(reader), stopWords);
   很明显它在实现了基本的分词和小写转换的基础上,还会将一些"停用词"从文档数据中删除。
   
   ** 注意:在StopAnalyzer类中定义了一个默认的停用词列表(数组),如果我们没有指定自己定义的停用词列表,
   那么运行的时候就会使用这个默认的停用词列表。
   这个默认停用词表是针对英文定义的。
   public static final String[] ENGLISH_STOP_WORDS = {
    "a", "an", "and", "are", "as", "at", "be", "but", "by",
    "for", "if", "in", "into", "is", "it",
    "no", "not", "of", "on", "or", "s", "such",
    "t", "that", "the", "their", "then", "there", "these",
    "they", "this", "to", "was", "will", "with"
   };
    
   ** 使用:如何使用我们自己定义的停用词呢?使用StopAnalyzer的构造方法传入定义好的停用词列表进行构建分析器对象即可。
   
   例子:
    //创建一个测试类
    public class StandardAnalyzerTest{
     //构造方法
     public StandardAnalyzerTest(){}
     
     //入口主方法
     public static void main(String[] args){
      //创建停用词
      String[] stopWords = {"always"};
      //初始化一个StandardAnalyzer对象
      Analyzer analyzer = new StopAnalyzer(stopWords);
      //创建进行分析测试的字符串数据信息
      StringReader reader = new StringReader("People are always talking about 'the problem of youth'.");
      
      //创建一个TokenStream对象
      TokenStream tokenStream = analyzer.tokenStream(reader);
      //作为辅助输出时显示的行号
      int i = 0;
      //调用TokenStream类next()方法得到分析器分词过滤后的封装有分词信息的Token对象
      Token token = tokenSream.next();
      while(token != null){
       //增加i行号值
       i++;
       //Token.termText()是分析器分词过滤后的分词内容
       System.out.println("行数 " + i + " : " + token.termText());
       //取得下一个Token分词,然后再次输出
       token = tokenStream.next();
      }
     }
    }
    ** 结果:将会看到只有always这个单词被删除了,即停用。
    上边构造StopAnalyzer的时候如果使用默认的构造方法,那么使用的将会是默认的停用词列表。
          
   ** 问题:能够移除停用词非常有益,不过同时也带来了另一个有趣的问题:就是移除所谓的停用词之后,所留下来的空间如何处理??
   
   ** 分析:这个问题就是会带来语义查询时可能会造成返回的结果不太准确,不过影响不大,如果感兴趣可以自研究。
   
   3.3 空格分析器----WhitespaceAnalyzer类:用于对空格进行切分的一个分析器。就是按照空格进行切词。
   它的tokenStream方法return new WhitespaceTokenizer(reader);就是只使用了空格分词器,而没有使用任何过滤器。
   
   例子:代码基本同上,修改一下代码:
   //初始化一个StandardAnalyzer对象
   Analyzer analyzer = new WhitespaceAnalyzer();
   //创建进行分析测试的字符串数据信息
   StringReader reader = new StringReader("Today is sunday,saturday and monday.");

   ** 测试结果为:
       Today
       is
       sunday,aturday
       and
       monday.
   
   ** 可以看出来只是按照空格分词,标点不好没有去掉,停用词也没有去掉。
          
   3.4 其他分析器:Lucene还提供了不少的分析器,例如SimpleAnalyzer、GermanAnalyzer等,这些就不一一列举了,
   感兴趣的自己学习。
   
          
          
          
          
          
          
          
          
          
          

原创粉丝点击