• 通过lucene的StandardAnalyzer分析器来了解分词


    本文转载http://blog.csdn.net/jspamd/article/details/8194919

       不同的Lucene分析器Analyzer,它对TokenStream进行分词的方法是不同的,这需要根据具体的语言来选择。比如英文,一般是通过空格来分割词条,而中文汉字则不能通过这种方式,最简单的方式就是单个汉字作为一个词条。

    TokenStream是通过从设备或者其他地方获取数据源而构造的一个流,我们要执行分词的动作,应该对这个TokenStream进行操作。

    TokenStream也可以不是直接通过数据源构造的流,可以是经过分词操作之后读入TokenFilter的一个分词流。

    从本地磁盘的文件读取文本内容,假定在文本文件shirdrn.txt中有下列文字:

    中秋之夜,享受着月华的孤独,享受着爆炸式的思维跃迁。

    通过使用FileReader构造一个流,对其进行分词:

    package org.shirdrn.lucene;
    
    import java.io.File;
    import java.io.FileReader;
    import java.io.Reader;
    
    import org.apache.lucene.analysis.Analyzer;
    import org.apache.lucene.analysis.Token;
    import org.apache.lucene.analysis.TokenStream;
    import org.apache.lucene.analysis.standard.StandardAnalyzer;
    
    public class MyAnalyzer {
    
    public static void main(String[] args) {
       try {
        File file = new File("E:\shirdrn.txt");
        Reader reader = new FileReader(file);
       Analyzer a = new StandardAnalyzer();
        //Analyzer a = new CJKAnalyzer();
        //Analyzer a = new ChineseAnalyzer();
        //Analyzer a = new WhitespaceAnalyzer();
        TokenStream ts = a.tokenStream("", reader);
        Token t = null;
        int n = 0;
        while((t = ts.next()) != null ){
         n ++ ;
         System.out.println("词条"+n+"的内容为 :"+t.termText());
        }
        System.out.println("== 共有词条 "+n+" 条 ==");
       } catch (Exception e) {
        e.printStackTrace();
       }
    }
    }

    这里使用StandardAnalyzer分析器,而且使用了不带参数的构造器StandardAnalyzer(),在StandardAnalyzer类的这个不带参数的构造器中,指定了一个过滤字符数组STOP_WORDS:

    public StandardAnalyzer() {
        this(STOP_WORDS);
    }

    而在StandardAnalyzer类中定义的STOP_WORDS 数组实际是引用StopAnalyzer类的ENGLISH_STOP_WORDS数组,该数组中可以根据需要添加过滤的字符:

    public static final String[] STOP_WORDS = StopAnalyzer.ENGLISH_STOP_WORDS;

    StopAnalyzer类中ENGLISH_STOP_WORDS数组原始内容如下所示:

    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", "such",
        "that", "the", "their", "then", "there", "these",
        "they", "this", "to", "was", "will", "with"
    };

    都是一些英文单词,而且这些单词对于检索关键字意义不大,所以在分析的时候应该把出现的这些单词过滤掉。

    如果按照默认的STOP_WORDS运行上面我们的测试程序,则根本没有对中文起到过滤作用,测试结果如下所示:

    词条1的内容为 :中
    词条2的内容为 :秋
    词条3的内容为 :之
    词条4的内容为 :夜
    词条5的内容为 :享
    词条6的内容为 :受
    词条7的内容为 :着
    词条8的内容为 :月
    词条9的内容为 :华
    词条10的内容为 :的
    词条11的内容为 :孤
    词条12的内容为 :独
    词条13的内容为 :享
    词条14的内容为 :受
    词条15的内容为 :着
    词条16的内容为 :爆
    词条17的内容为 :炸
    词条18的内容为 :式
    词条19的内容为 :的
    词条20的内容为 :思
    词条21的内容为 :维
    词条22的内容为 :跃
    词条23的内容为 :迁
    == 共有词条 23 条 ==

    我们可以在org.apache.lucene.analysis.StopAnalyzer类中定制自己的STOP_WORDS,例如我们定义:

    public static final String[] ENGLISH_STOP_WORDS = {
         "着", "的", "之", "式"
       };

    则再执行上面的测试程序,分词过程中会过滤掉出现在ENGLISH_STOP_WORDS数组中的词条,如下所示:

    词条1的内容为 :中
    词条2的内容为 :秋
    词条3的内容为 :夜
    词条4的内容为 :享
    词条5的内容为 :受
    词条6的内容为 :月
    词条7的内容为 :华
    词条8的内容为 :孤
    词条9的内容为 :独
    词条10的内容为 :享
    词条11的内容为 :受
    词条12的内容为 :爆
    词条13的内容为 :炸
    词条14的内容为 :思
    词条15的内容为 :维
    词条16的内容为 :跃
    词条17的内容为 :迁
    == 共有词条 17 条 ==

    另外,因为StandardAnalyzer类具有很多带参数的构造函数,可以在实例化一个StandardAnalyzer的时候,通过构造函数定制分析器,例如使用:

    public StandardAnalyzer(Set stopWords)

    构造的分析器如下:

       Set stopWords = new HashSet();
        stopWords.add("着");
        stopWords.add("的");
        stopWords.add("之");
        stopWords.add("式");
        Analyzer a = new StandardAnalyzer(stopWords);

    运行结果同修改StopAnalyzer类中的STOP_WORDS结果是一样的。

    还有一个构造函数,通过使用数组指定stopWords的过滤词条:

    public StandardAnalyzer(String[] stopWords) {
        stopSet = StopFilter.makeStopSet(stopWords);
    }

    调用了StopFilter类的makeStopSet方法对stopWords中的字符进行了转换处理:

    public static final Set makeStopSet(String[] stopWords) {
        return makeStopSet(stopWords, false);
    }

    又调用了该类的一个重载的方法makeStopSet,第一个参数指定过滤词条的数组,第一个参数为boolean类型,设置是否要将大写字符转换为小写:

    public static final Set makeStopSet(String[] stopWords, boolean ignoreCase) {
        HashSet stopTable = new HashSet(stopWords.length);
        for (int i = 0; i < stopWords.length; i++)
          stopTable.add(ignoreCase ? stopWords[i].toLowerCase() : stopWords[i]);
        return stopTable;
    }

    在StandardAnalyzer类中,没有把stopWords中的词条转换为小写。

    上面的三种构造StandardAnalyzer分析器的方式都是在程序中指定要过滤词条,程序的独立性比较差,因为每次想要添加过滤词条都需要改动程序。

    StandardAnalyzer还提供了两种从数据源读取过滤词条的文本的构造方式:

    public StandardAnalyzer(File stopwords) throws IOException {
        stopSet = WordlistLoader.getWordSet(stopwords);
    }
    
    public StandardAnalyzer(Reader stopwords) throws IOException {
        stopSet = WordlistLoader.getWordSet(stopwords);
    }

    他们分别使用File和Reader分别来构造一个File对象和读取字符流,从指定的数据源读取内容,然后调用WordlistLoader类的getWordSet静态方法来对读取的字符流进行转换操作,以从File对象中获取字符为例:

    public static HashSet getWordSet(File wordfile) throws IOException {
        HashSet result = new HashSet();
        FileReader reader = null;
        try {
          reader = new FileReader(wordfile);
          result = getWordSet(reader);
        }
        finally {
          if (reader != null)
            reader.close();
        }
        return result;
    }

    实际上仍然通过File对象构造一个FileReader读取字符流,然后从流中取得过滤的词条,加入到HashSet 中。这里调用了获取HashSet的getWordSet方法,在方法getWordSet中才真正地实现了提取词条的操作:

    public static HashSet getWordSet(Reader reader) throws IOException {
        HashSet result = new HashSet();
        BufferedReader br = null;
        try {
          if (reader instanceof BufferedReader) {
            br = (BufferedReader) reader;
          } else {
            br = new BufferedReader(reader);
          }
          String word = null;
          while ((word = br.readLine()) != null) {
            result.add(word.trim());
          }
        }
        finally {
          if (br != null)
            br.close();
        }
        return result;
    }

    这里提取词条要求读入的文本是按照行来分割过滤词条的,即每行作为一个词条。对于中文,只能是每个字作为一行,如果以两个的词语作为一行,处理后根本没有加入到过滤词条的HashSet中,这时因为StandardAnalyzer分析器是以单个中文汉字作为一个词条的。我们可以定制自己的分析器。

    测试一下上述说明的情况。

    在本地磁盘上建立一个txt文本stopWords.txt,添加过滤词条:

    着
    的
    之
    式

    测试程序如下所示:

       public static void main(String[] args) {
       try {
        File file = new File("E:\shirdrn.txt");
        FileReader stopWords = new FileReader("E:\stopWords.txt");
        Reader reader = new FileReader(file);   
        Analyzer a = new StandardAnalyzer(stopWords);
        TokenStream ts = a.tokenStream("", reader);
        Token t = null;
        int n = 0;
        while((t = ts.next()) != null ){
         n ++ ;
         System.out.println("词条"+n+"的内容为 :"+t.termText());
        }
        System.out.println("== 共有词条 "+n+" 条 ==");
       
       } catch (Exception e) {
        e.printStackTrace();
       }
    }

    测试输出结果同前面的一样,都对词条进行了过滤:

    词条1的内容为 :中
    词条2的内容为 :秋
    词条3的内容为 :夜
    词条4的内容为 :享
    词条5的内容为 :受
    词条6的内容为 :月
    词条7的内容为 :华
    词条8的内容为 :孤
    词条9的内容为 :独
    词条10的内容为 :享
    词条11的内容为 :受
    词条12的内容为 :爆
    词条13的内容为 :炸
    词条14的内容为 :思
    词条15的内容为 :维
    词条16的内容为 :跃
    词条17的内容为 :迁
    == 共有词条 17 条 ==
  • 相关阅读:
    查找正序排列的List中缺失的日期数据的一个算法
    了解下C#异常时的输出
    Environment.Exit(0) 、Application.Exit() 、this.Close() 、this.Dispose()的区别
    java--多线程之后台线程
    java--线程状态【转】
    DRP项目总结
    Java实现 蓝桥杯 算法训练 矩阵乘法
    Java实现 蓝桥杯 算法训练 矩阵乘法
    Java实现 蓝桥杯 算法训练 矩阵乘法
    Java实现 蓝桥杯 算法训练 大小写转换
  • 原文地址:https://www.cnblogs.com/hwaggLee/p/4959772.html
Copyright © 2020-2023  润新知