Lucene快速入门第三讲——看看Lucene是如何支持中文分词的?

在这一讲中,我们要看看Lucene到底是如何支持中文分词的?为了向大家阐述明白这个问题,咱们可先从分析器的执行过程入手。

分析器(Analyzer)的执行过程

如下图所示是语汇单元的生成过程:
这里写图片描述
从一个Reader字符流开始,创建一个基于Reader的Tokenizer分词器,经过三个TokenFilter生成语汇单元Token。

要看分析器的分析效果,只需要看TokenStream中的内容就可以了。每个分析器都有一个tokenStream方法,返回的就是一个TokenStream对象。

标准分析器的分词效果

之前我们在创建索引库的时候,就用到了官方推荐的标准分析器,也就是org.apache.lucene.analysis.standard.StandardAnalyzer。现在,我们可以在一个单元测试类(比如FirstLucene.java)中编写一个如下的方法,来重新看看它的分词效果。

public class LuenceFirst {

	// 查看标准分析器的分词效果
	@Test
	public void testTokenStream() throws Exception {
		// 创建一个标准分析器对象
		Analyzer analyzer = new StandardAnalyzer();
		// 获得tokenStream对象
				// 第一个参数:域名,可以随便给一个
				// 第二个参数:要分析的文本内容
		TokenStream tokenStream = analyzer.tokenStream("test", 
				"The Spring Framework provides a comprehensive programming and configuration model.");
		// 添加一个引用,可以获得每个关键词
		CharTermAttribute charTermAttribute = tokenStream.addAttribute(CharTermAttribute.class);
		// 添加一个偏移量的引用,记录了关键词的开始位置以及结束位置
		OffsetAttribute offsetAttribute = tokenStream.addAttribute(OffsetAttribute.class);
		// 将指针调整到列表的头部
		tokenStream.reset();
		// 遍历关键词列表,通过incrementToken方法判断列表是否结束
		while(tokenStream.incrementToken()) {
			// 关键词的起始位置
			System.out.println("start->" + offsetAttribute.startOffset());
			// 取关键词
			System.out.println(charTermAttribute);
			// 结束位置
			System.out.println("end->" + offsetAttribute.endOffset());
		}
		tokenStream.close();
	}
	
}

这里我还是要说明一点,上述方法并不需要我们生写出来,因为它涉及到了C++语言方面的知识,所以说你不懂也没有问题,只须知道以上方法的作用是来查看标准分析器的分词效果的。运行以上方法,你将会在Eclipse控制台中看到如下打印内容。
在这里插入图片描述
从上图中我们可以清楚地看到当前的关键词,以及该关键词的起始位置和结束位置。

中文分析器分析

Lucene自带中文分词器

在这里插入图片描述
现在我们主要来看看第三个中文分析器的分词效果。相比较前两个中文分析器,SmartChineseAnalyzer绝对要胜出一筹。为了观看其分词效果,我们可将FirstLucene单元测试类中的testTokenStream方法改造为下面这个样子。

public class FirstLucene {

	// 查看智能中文分析器的分词效果
	@Test
	public void testTokenStream() throws Exception {
		Analyzer analyzer = new SmartChineseAnalyzer(); // 智能中文分析器
		// Analyzer analyzer = new IKAnalyzer();
		// 获得tokenStream对象
				// 第一个参数:域名,可以随便给一个
				// 第二个参数:要分析的文本内容
		TokenStream tokenStream = analyzer.tokenStream("test", 
				"可以用二维表结构来逻辑表达实现的数据");
		// 添加一个引用,可以获得每个关键词
		CharTermAttribute charTermAttribute = tokenStream.addAttribute(CharTermAttribute.class);
		// 添加一个偏移量的引用,记录了关键词的开始位置以及结束位置
		OffsetAttribute offsetAttribute = tokenStream.addAttribute(OffsetAttribute.class);
		// 将指针调整到列表的头部
		tokenStream.reset();
		// 遍历关键词列表,通过incrementToken方法判断列表是否结束
		while(tokenStream.incrementToken()) {
			// 关键词的起始位置
			System.out.println("start->" + offsetAttribute.startOffset());
			// 取关键词
			System.out.println(charTermAttribute);
			// 结束位置
			System.out.println("end->" + offsetAttribute.endOffset());
		}
		tokenStream.close();
	}

}

温馨提示:记得在工程中导入lucene-analyzers-smartcn-8.4.0.jar。运行以上方法,你将会在Eclipse控制台中看到如下打印内容。
在这里插入图片描述
虽然SmartChineseAnalyzer分析器对中文支持较好,但是扩展性差,扩展词库、禁用词库和同义词库等不好处理。故在实际开发中我们也是弃用的,取而代之的是第三方中文分析器。

第三方中文分析器

第三方中文分析器有很多,下面我会一一介绍到,虽然很多,但是大部分都已过时,用到的只是那一个而已,至于是哪一个,后面会揭晓。

paoding

庖丁解牛最新版在https://code.google.com/p/paoding/,其最多只支持Lucene 3.0,且最新提交的代码在2008-06-03,在svn中最新也是2010年提交,已经过时,不予考虑。

mmseg4j

最新版已从https://code.google.com/p/mmseg4j/移至https://github.com/chenlb/mmseg4j-solr,支持Lucene 4.10,且在github中最新提交代码是2014年6月,从09年~14年一共有18个版本,也就是一年几乎有3个大小版本,有较大的活跃度,用了mmseg算法。

IK-analyzer

最新版在https://code.google.com/p/ik-analyzer/上,支持Lucene 4.10,从2006年12月推出1.0版开始,IKAnalyzer已经推出了4个大版本。最初,它是以开源项目Luence为应用主体的,结合词典分词和文法分析算法的中文分词组件。从3.0版本开始,IK发展为面向Java的公用分词组件,独立于Lucene项,同时提供了对Lucene的默认优化实现。在2012版本中,IK实现了简单的分词歧义排除算法,标志着IK分词器从单纯的词典分词向模拟语义分词衍化。但是也就在2012年12月后没有再更新了。

ansj_seg

最新版本在https://github.com/NLPchina/ansj_seg,tags仅有1.1版本,从2012年到2014年更新了大小6次,但是作者本人在2014年10月10日说明:“可能我以后没有精力来维护ansj_seg了”,现在由"nlp_china"管理。2014年11月有更新。并未说明是否支持Lucene,是一个由CRF(条件随机场)算法所做的分词算法。

imdict-chinese-analyzer

最新版在https://code.google.com/p/imdict-chinese-analyzer/,最新更新也在2009年5月,可下载源码,不支持Lucene 4.10。它利用的是HMM(隐马尔科夫链)算法。

Jcseg

最新版本在git.oschina.net/lionsoul/jcseg,支持Lucene 4.10,作者有较高的活跃度。其利用的是mmseg算法。

小结

虽然介绍了这么的第三方中文分析器,但是在这里,我使用的是IK-analyzer,所以下面的讲解也是围绕着该中文分析器来进行的。

IK-analyzer中文分析器的使用

如果你想要使用IK-analyzer,那么必然得下载其相关东西,例如jar包、核心配置文件以及扩展词典和停用词词典等。随着你使用的Lucene版本越来越高,相应地IK-analyzer的jar包的版本也要高一点才行,不然的话,就会有各种各样的问题,因为这些问题本人就碰到过。例如,由于本系列教程中所使用的Lucene是Lucene 8.4.0这个版本,所以我下载的就是ik-analyzer-7.6.0.jar这个jar包。
在这里插入图片描述
那么IK-analyzer中文分析器该怎么使用呢?IK-analyzer中文分析器的使用步骤如下:

  1. 把ik-analyzer-7.6.0.jar包添加到工程中;
  2. 把配置文件、扩展词典和停用词词典添加到classpath下。有一点需要注意:扩展词典和停用词词典这两个文件的字符集一定要保证是UTF-8字符集,注意是无BOM的UTF-8编码,严禁使用Windows的记事本编辑。

下面我们来看看IK-analyzer这个第三方中文分析器的分词效果。现在随着互联网的日趋发展,网络用语层出不穷,例如"高富帅","白富美"等等,像这样的网络用语是不需要进行分词的,而是当作一个整体的关键词,这样像这种不用分词的网络用语就应该存储在扩展词典中。为了清楚地观察IK-analyzer这个第三方中文分析器的分词效果,可以在扩展词典中添加"高富帅"这一网络用语,如下图所示。
在这里插入图片描述
然后将FirstLucene单元测试类中的testTokenStream方法改造为下面这个样子。

public class FirstLucene {

	// 查看第三方中文分析器的分词效果
	@Test
	public void testTokenStream() throws Exception {
		// 创建一个第三方中文分析器对象
		Analyzer analyzer = new IKAnalyzer();
		// 获得tokenStream对象
				// 第一个参数:域名,可以随便给一个
				// 第二个参数:要分析的文本内容
		TokenStream tokenStream = analyzer.tokenStream("test", 
				"高富帅可以用二维表结构来逻辑表达实现的数据");
		// 添加一个引用,可以获得每个关键词
		CharTermAttribute charTermAttribute = tokenStream.addAttribute(CharTermAttribute.class);
		// 添加一个偏移量的引用,记录了关键词的开始位置以及结束位置
		OffsetAttribute offsetAttribute = tokenStream.addAttribute(OffsetAttribute.class);
		// 将指针调整到列表的头部
		tokenStream.reset();
		// 遍历关键词列表,通过incrementToken方法判断列表是否结束
		while(tokenStream.incrementToken()) {
			// 关键词的起始位置
			System.out.println("start->" + offsetAttribute.startOffset());
			// 取关键词
			System.out.println(charTermAttribute);
			// 结束位置
			System.out.println("end->" + offsetAttribute.endOffset());
		}
		tokenStream.close();
	}

}

注意:千万要记得在IKAnalyzer.cfg.xml核心配置文件中配置好自己的扩展字典和停用词词典。
在这里插入图片描述
接着,运行以上方法,你将会在Eclipse控制台看到如下打印内容。
在这里插入图片描述
从上图可以清楚地看出"高富帅"这一网络用语并没有分词,这正是我们所期望的结果。

除此之外,对于一些敏感的词,如"安拉",像这样的敏感词汇就不应该出现在单词列表中,所以可将这种敏感词汇存储在停用词词典中,如下图所示。
在这里插入图片描述
然后将FirstLucene单元测试类中的testTokenStream方法改造为下面这个样子。

public class FirstLucene {

	// 查看第三方中文分析器的分词效果
	@Test
	public void testTokenStream() throws Exception {
		// 创建一个第三方中文分析器对象
		Analyzer analyzer = new IKAnalyzer();
		// 获得tokenStream对象
				// 第一个参数:域名,可以随便给一个
				// 第二个参数:要分析的文本内容
		TokenStream tokenStream = analyzer.tokenStream("test", 
				"安拉,高富帅可以用二维表结构来逻辑表达实现的数据");
		// 添加一个引用,可以获得每个关键词
		CharTermAttribute charTermAttribute = tokenStream.addAttribute(CharTermAttribute.class);
		// 添加一个偏移量的引用,记录了关键词的开始位置以及结束位置
		OffsetAttribute offsetAttribute = tokenStream.addAttribute(OffsetAttribute.class);
		// 将指针调整到列表的头部
		tokenStream.reset();
		// 遍历关键词列表,通过incrementToken方法判断列表是否结束
		while(tokenStream.incrementToken()) {
			// 关键词的起始位置
			System.out.println("start->" + offsetAttribute.startOffset());
			// 取关键词
			System.out.println(charTermAttribute);
			// 结束位置
			System.out.println("end->" + offsetAttribute.endOffset());
		}
		tokenStream.close();
	}

}

接着,运行以上方法,你将会在Eclipse控制台看到如下打印内容。
在这里插入图片描述
从上图可知,像"安拉"这样的敏感词汇并没有出现在单词列表中。

分析器的应用场景

索引时使用Analyzer

输入关键字进行搜索,当需要让该关键字与文档域内容所包含的词进行匹配时需要对文档域内容进行分析,需要经过Analyzer分析器处理生成语汇单元(Token)。分析器分析的对象是文档中的Field域。当Field的tokenized(是否分词)属性为true时会对Field值进行分析,如下图所示。
这里写图片描述
对于满足如下两点的一些Field可以不用分析:

  1. 不作为查询条件的内容,比如文件路径;
  2. 不是匹配内容中的词而匹配Field的整体内容,比如订单号、身份证号等。

搜索时使用Analyzer

对搜索关键字进行分析和索引分析一样,使用Analyzer对搜索关键字进行分析、分词处理,使用分析后的每个词语进行搜索。比如,搜索关键字spring web,经过分析器进行分词,得出spring web,拿词去索引词典表中查找,找到索引链接到Document,解析Document内容。

对于匹配整体Field域的查询可以在搜索时不分析,比如根据订单号、身份证号查询等。

小结

搜索使用的分析器要和索引使用的分析器保持一致(最好)。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

李阿昀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值