zoukankan      html  css  js  c++  java
  • 通过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 条 ==
  • 相关阅读:
    【Azure Redis 缓存】Azure Redis 功能性讨论二
    【Azure Developer】如何用Microsoft Graph API管理AAD Application里面的Permissions
    【Azure 环境】通过Python SDK收集所有订阅简略信息,例如订阅id 名称, 资源组及组内资源信息等,如何给Python应用赋予相应的权限才能获取到信息呢?
    【Azure 应用服务】App Service与APIM同时集成到同一个虚拟网络后,如何通过内网访问内部VNET的APIM呢?
    【Azure 云服务】如何从Azure Cloud Service中获取项目的部署文件
    【Azure Redis 缓存】Azure Redis 异常
    【Azure 微服务】基于已经存在的虚拟网络(VNET)及子网创建新的Service Fabric并且为所有节点配置自定义DNS服务
    【Azure Redis 缓存】遇见Azure Redis不能创建成功的问题:至少一个资源部署操作失败,因为 Microsoft.Cache 资源提供程序未注册。
    【Azure Redis 缓存】如何得知Azure Redis服务有更新行为?
    【Azure API 管理】在 Azure API 管理中使用 OAuth 2.0 授权和 Azure AD 保护 Web API 后端,在请求中携带Token访问后报401的错误
  • 原文地址:https://www.cnblogs.com/hwaggLee/p/4959772.html
Copyright © 2011-2022 走看看