[lucene]05.分词器的区别以及通过获取所有terms实现内容关键词提取

       前边我们看到,不管是使用StandardAnalyzer和SmartChineseAnalyzer分词器,通过luke看到的中文都是显示为乱码,这里只是显示的问题,不影响真正的分词。那么我们怎么在程序里面获取经过lucene索引后分出的terms呢?也就是怎么查看分词结果呢?首先,我先来解释一下分词器,lucene的分词器有很多种,除了我上面用到的StandardAnalyzer,还有SimpleAnalyzer、KeywordAnalyzer等数十种,而SmartChineseAnalyzer分词器是由中国人自己实现的lucene接口,并不属于lucene自带的分词器,为什么要有这么多的分词器呀,让我们感到头大,各种分词器的区别又是什么?

        想解决上面抛出的这么多问题,非得下一番苦功夫,说真的,大部分分词器我也没用过,就像apache的很多模块一样,也许真的很少有人用到如此之多的分词器,我们需要知道,我感觉需要知道的,只是为什么要有这么多分词器罢了,至于如何去使用每一个分词器就遇到了再去深究吧。那么为什么要有这么多分词?比如说,对于一篇英文文本来说,我怎么得到里面的各个单词,一般的使用空格就可以了,你可以简单的认为StandardAnalyzer就是这样来分词的。中文呢,如果也用空格来分的话,就是一个个的汉字了,汉字词语怎么分呢,这个有点难,虽然英文也有短语可能包含几个单词,相比之下,中文的词语的分词难度还是更胜一筹的,为了尽可能准确的得到中文的词语,就有了中文分词器,你可以简单的认为SmartChineseAnalyzer就是这个作用,至于它是怎么区分中文词语的,自然有它自己的算法,你只需要使用就行了。

        好,下面我们来看如何获取使用StandardAnalyzer分词器和使用SmartChineseAnalyzer分词器的分词结果:

package cn.zhao.cms.column;

import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Paths;

import org.apache.commons.io.FileUtils;
import org.apache.lucene.analysis.cn.smart.SmartChineseAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.StringField;
import org.apache.lucene.document.TextField;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.index.MultiFields;
import org.apache.lucene.index.Term;
import org.apache.lucene.index.Terms;
import org.apache.lucene.index.TermsEnum;
import org.apache.lucene.queryparser.classic.ParseException;
import org.apache.lucene.search.highlight.InvalidTokenOffsetsException;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.util.BytesRef;

public class TestLucene {
	public static void main(String[] args) throws Exception{
		createIndex();
		 searchIndex();
	}
//获取某个域分词后的terms
	private static void searchIndex() throws IOException, ParseException,
			InvalidTokenOffsetsException {
		    Directory directory = FSDirectory.open(Paths.get("D:\\lucene\\index"));
		    IndexReader ir=DirectoryReader.open(directory);
		  //获取某个域分词后的terms
		    Terms terms = MultiFields.getTerms(ir, "content");
		    TermsEnum iterator = terms.iterator();
		    BytesRef bRef = null;
		    int rank=1;
			while ((bRef=iterator.next())!=null) {
				String oneTerm=new String(bRef.bytes, bRef.offset, bRef.length,Charset.forName("utf-8"));
				//docFreq:当前这个term在几个document里面出现了
				System.out.println("第"+rank+"个term是:"+oneTerm+",共在:"+iterator.docFreq()
						+"个文档出现,该term在所有文档中共出现:"
						+ir.totalTermFreq(new Term("content", bRef))+"次");
				rank++;
			}
		    ir.close();
		    directory.close();
	}
//创建索引
	private static void createIndex() throws IOException {
		//指定存放lucene生成的索引的目录,可以把这个目录看做一张表,lucene的所有索引都存放在这个表中
	    Directory directory = FSDirectory.open(Paths.get("D:\\lucene\\index"));
	    //使用默认的英文分词器
//	    Analyzer analyzer=new StandardAnalyzer();
	    //使用对中文支持更好的分词器
	    SmartChineseAnalyzer analyzer=new SmartChineseAnalyzer();
	    IndexWriterConfig conf=new IndexWriterConfig(analyzer);
	    IndexWriter iwriter =new IndexWriter(directory, conf);
	    //每次创建先删除原来的已经生成的索引
		   iwriter.deleteAll();
		   //deleteAll只是放进了lucene索引回收站,这里还要从回收站也删除掉
		    iwriter.forceMergeDeletes();
	    //这里我索引了D:\\lucene\\data目录下的所有文件
	    File dirFile=new File("D:\\lucene\\data");
	    File[] listFiles =dirFile.listFiles();
	    for(int i=0;i<listFiles.length;i++){
	    	File file=listFiles[i];
	    	//Document可以看做表的一条记录,记录自然有多个field,可以看做表里的字段
	    	//field在lucene里叫域,一个域有n(>=1)个terms,这个terms没法类比关系型数据库里面的概念了,
	    	//你可以理解每个字段的内容按一定的切分规则(分词器)切分成了若干单元,每个单元就叫做term
	    	Document document=new Document();
	    	document.add(new StringField("id", 0+"", Field.Store.YES));
	    	document.add(new StringField("docurl", file.getAbsolutePath(), Field.Store.YES));
	    	TextField textField = new TextField("content", FileUtils.readFileToString(file), Field.Store.NO);
	    	document.add(textField);
	    	//按指定的分词规则analyzer写入存放索引的目录D:\\lucene\\index
	    	iwriter.addDocument(document);
	    }
	    System.out.println("实际索引多少个文档:"+iwriter.numDocs());	    
	    System.out.println("最大索引多少个文档:"+iwriter.maxDoc());	    
	    //最后记得关闭资源
	    iwriter.close();
	    directory.close();
	}
}

代码说明:

        1.为了程序的完整性,我把创建索引的也贴了出来,其实如何获取所有分词后的terms的关键代码只有上面的如下一段:

  //获取某个域分词后的terms
Terms terms = MultiFields.getTerms(ir, "content");
TermsEnum iterator = terms.iterator();
BytesRef bRef = null;
int rank=1;
while ((bRef=iterator.next())!=null) {
	String oneTerm=new String(bRef.bytes, bRef.offset, bRef.length,Charset.forName("utf-8"));
	//docFreq:当前这个term在几个document里面出现了
	System.out.println("第"+rank+"个term是:"+oneTerm+",共在:"+iterator.docFreq()
			+"个文档出现,该term在所有文档中共出现:"
			+ir.totalTermFreq(new Term("content", bRef))+"次");
	rank++;
}

        2.在上边的createIndex()函数中,修改使用的分词器,当使用SmartChineseAnalyzer analyzer=new SmartChineseAnalyzer();的时候,结果如下图:

image.png

image.png

总共分出了3769个term,这就是分词的结果。

而在createIndex()函数中打开Analyzer analyzer=new StandardAnalyzer();这句的注释,就是使用StandardAnalyzer这个标准分词器的时候,结果如下:

image.png

可以看到最终分词结果公有2409个terms,而且并没有把在文本中出现的中文词语给切分出来,比如和上面的图片结果对比,可以发现“鬼鬼祟祟”、“麻烦”等这些词语在下面的图片中都是没有的。

好,这就是分词的区别了,相信有了这个直观的感受,你应该就清楚了吧。

至于如何提取一篇文章的关键词,我想就不难了吧,为什么?因为这篇文章里面的词语你已经知道了,你每个词语出现多少次你也知道了,把次数从高到低排下序,取前10个或者前5个就是关键词了呀,这个我相信各位一定能理解,这个小功能各位也一定能写好。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值