IKAnalyzer非常流行的中文分词器,对中文切词有兴趣的朋友可以看看。
基本所有的框架都分两部分,一是:框架的初始化,也就是框架启动;二是:调用框架,让框架为我们做一些事。我们今天先来看看一下IK的初始化过程。
IKAnalyzer可以说一个非常流行的分词器了,但我觉得IKAnalyzer的代码写一般。
废话就不多说了,直接看源码吧。IKAnalyzer挂在Google上,直接到google下就好了,文档之类的都很全。
假设1,你用过IKAnalyzer,对IKAnalyzer有了解。或者,你想了解分词器。
假设2,你已经下到源码,建了一个Java SE的项目。
假如,如果你满足两个假设,那就一起来读读IKAnalyzer的源码吧。
新建一个类,主方法的内容如下:
public static void main(String[] args){
IKSegmenter seg = new IKSegmenter(new StringReader("中国人民"), true);
try {
Lexeme lex = seg.next();
while(lex != null){
System.out.println(lex);
lex = seg.next();
}
} catch (IOException e) {
e.printStackTrace();
}
}
PS:我们从IKSegment而不是从IKAnalyzer开始,如果从IKAnalyzer这个类开始,那么我们需要再加入Lucene3.1以上(Lucene4.0以下)的包。而且,基本都上Lucene的内容,暂时不关注,可以自行了解。
强调一下,IKAnalyzer的作者非常有心,看过源码的朋友应该知道,他注释很多。先赞一个。
从IKSegment的构造方法进来,
/**
* IK分词器构造函数
* @param input
* @param useSmart 为true,使用智能分词策略
*
* 非智能分词:细粒度输出所有可能的切分结果
* 智能分词: 合并数词和量词,对分词结果进行歧义判断
*/
public IKSegmenter(Reader input , boolean useSmart){
this.input = input;
this.cfg = DefaultConfig.getInstance();
this.cfg.setUseSmart(useSmart);
this.init();
}
首先看 DefaultConfig.getInstance() 里面是这样的:
/**
* 返回单例
* @return Configuration单例
*/
public static Configuration getInstance(){
return new DefaultConfig();
}
由于IKSegment的初始化实际上是IK自己做的,在IKTokenizer。因此,这种写法完全没有问题,它初始化次数绝对是串行。假如,我说假如哈。假如这个东西是并行,这个单例的写法就不合适了,会造成多次初始化。有兴趣的话要以比对着Dictionary.initial(this.cfg)看。当然,这不是我们今天关注的内容。
同时这个地方做资源初始化,也就是解析配置文件,有兴趣的朋友可以看看。
进到 this.init() 方法,代码也是中规中矩的没什么难点,但这是我们今天的关注点。从代码上可以看到,首先初始化了词典,然后初始化分词环境,……
/**
* 初始化
*/
private void init(){
//初始化词典单例
Dictionary.initial(this.cfg);
//初始化分词上下文
this.context = new AnalyzeContext(this.cfg);
//加载子分词器
this.segmenters = this.loadSegmenters();
//加载歧义裁决器
this.arbitrator = new IKArbitrator();
}
在词典的初始化过程,
/**
* 加载主词典及扩展词典
*/
private void loadMainDict(){
//建立一个主词典实例
_MainDict = new DictSegment((char)0);
//读取主词典文件
InputStream is = this.getClass().getClassLoader().getResourceAsStream(cfg.getMainDictionary());
if(is == null){
throw new RuntimeException("Main Dictionary not found!!!");
}
try {
// 这里转码,原因是我们的词典都是UTF-8,但运行环境不一定。
BufferedReader br = new BufferedReader(new InputStreamReader(is , "UTF-8"), 512);
String theWord = null;
do {
//到了这里,我建议大家先看一目词典文件(/src/org/wltea/analyzer/dic/main2012.dic)。现在,我们基本了解词典文件是怎么样的了。因此,你也就知道,它为什么是一行一行读取的了。
theWord = br.readLine();
if (theWord != null && !"".equals(theWord.trim())) {
// toLowerCase()是规范化,毕竟索引不分大小写。
_MainDict.fillSegment(theWord.trim().toLowerCase().toCharArray());
}
} while (theWord != null);
}
……
}
我觉得这段,如果按下来这么写兴许更好一些,你们觉得呢。
String theWord = br.readLine();
while(theWord != null){
if(!theWord.trim().isEmpty()){
_MainDict.fillSegment(theWord.trim().toLowerCase().toCharArray());
}
}
由_MainDict.fillSegment(theWord.trim().toLowerCase().toCharArray());进来,历经千辛万苦来到这里。下面的代码可以谓重中之得哈。有一个数据结构叫,字典树。不管你知不知道,它都在下面了。这里简单说一下吧,详情请登陆www.baidu.com。
字典树,跟什么B树啊、B+树啊、B-树都差不多,因此跟二叉树不大一样。它是这样的,它广度不定,小于字典字数(“一二三一”算三个字)。然后,节点带有一个状态,即是不是完整词、词性之类的。
最最重要的一点,它的字必须是重复引用的。即有一个拥有字典全部单字的集合。比如,对一个英文字词,它有一个集合放里26个字母,然后所有的节点的都从这个集合引用,从而降低内存的开销。
也就是说,它一棵广度不定、深度不定,并引用来自同一集合的对象的树。我能说的,这就这么多了,剩下的交给百度吧。
看一下下面的代码,也都比较简单,就是字典树的实现。
private synchronized void fillSegment(char[] charArray , int begin , int length , int enabled){
// 装箱,然后获取字典表中的文字对象
Character beginChar = new Character(charArray[begin]);
Character keyChar = charMap.get(beginChar);
//字典中没有该字,则将其添加入字典表
if(keyChar == null){
charMap.put(beginChar, beginChar);
keyChar = beginChar;
}
//搜索当前节点的存储,查询对应keyChar的keyChar,如果没有则创建。如:已有“中国”,再来一个“中国人”的时候,它首先发现“中”已存在,那就直接,一点也不客气。若没有,那只能创建了。
DictSegment ds = lookforSegment(keyChar , enabled);
if(ds != null){
//处理keyChar对应的segment
if(length > 1){
//词元还没有完全加入词典树
ds.fillSegment(charArray, begin + 1, length - 1 , enabled);
}else if (length == 1){
//已经是词元的最后一个char,设置当前节点状态为enabled,
//enabled=1表明一个完整的词,enabled=0表示从词典中屏蔽当前词
ds.nodeState = enabled;
}
}
}
这段代码非常坑爹,但能提高效率。从这里可以知道,作者在效率方面下很大的功夫。有得有失,因此代码很复杂。中心思想是数组的效率比集合框架的效率高。具体是这样的,当前节点的宽度不大于ARRAY_LENGTH_LIMIT(默认是3)时,用数组来存;当宽度大于时,用HashMap来存。
/**
* 查找本节点下对应的keyChar的segment
*
* @param keyChar
* @param create =1如果没有找到,则创建新的segment ; =0如果没有找到,不创建,返回null
* @return
*/
rivate DictSegment lookforSegment(Character keyChar , int create){
DictSegment ds = null;
if(this.storeSize <= ARRAY_LENGTH_LIMIT){
//获取数组容器,如果数组未创建则创建数组
DictSegment[] segmentArray = getChildrenArray();
//搜寻数组
DictSegment keySegment = new DictSegment(keyChar);
int position = Arrays.binarySearch(segmentArray, 0 , this.storeSize, keySegment);
if(position >= 0){
ds = segmentArray[position];
}
//遍历数组后没有找到对应的segment
if(ds == null && create == 1){
ds = keySegment;
if(this.storeSize < ARRAY_LENGTH_LIMIT){
//数组容量未满,使用数组存储
segmentArray[this.storeSize] = ds;
//segment数目+1
this.storeSize++;
Arrays.sort(segmentArray , 0 , this.storeSize);
}else{
//数组容量已满,切换Map存储
//获取Map容器,如果Map未创建,则创建Map
Map<Character , DictSegment> segmentMap = getChildrenMap();
//将数组中的segment迁移到Map中
migrate(segmentArray , segmentMap);
//存储新的segment
segmentMap.put(keyChar, ds);
//segment数目+1 , 必须在释放数组前执行storeSize++ , 确保极端情况下,不会取到空的数组
this.storeSize++;
//释放当前的数组引用
this.childrenArray = null;
}
}
}else{
//获取Map容器,如果Map未创建,则创建Map
Map<Character , DictSegment> segmentMap = getChildrenMap();
//搜索Map
ds = (DictSegment)segmentMap.get(keyChar);
if(ds == null && create == 1){
//构造新的segment
ds = new DictSegment(keyChar);
segmentMap.put(keyChar , ds);
//当前节点存储segment数目+1
this.storeSize ++;
}
}
return ds;
接下来,还会加载量词词典及扩展词典,内容基本一样,自己看看就好了。整个过程也就优化这个位置有复杂,其它都还好,主要是要知道作者为什么要用先数组再用集合,这个地方清楚了,也就没啥问题。
后续,作者在用数组装的时候,用了折半搜索。我个人觉得,你竟然用了“折半查找”,为什么要长度设为3呢?这样首配对元素1,再配对元素2,最后配对元素3,对吧?而我们顺序查找也是这样的,所以我觉得这个参数值不大合理。或许作者有其它想法,我还没有参悟。
字典加载完了,回到IKSegmenter#init()继续下一步,即是初始化上下文环境。这个环境,分词器分词的环境,它提供分词基本条件和记录着分词状态。比如,需要分词的东西、分好词的词片、分词进到哪个位置等等等
。对上下文有兴趣的可以看看。
分享到:
相关推荐
**IKAnalyzer概述** IKAnalyzer是一款基于Java实现的开源中文分词器,主要应用于搜索引擎和文本分析领域。它是由尹国平(iK)发起并维护的项目,旨在提供一个高效且易用的中文分词解决方案。IKAnalyzer支持自定义...
通过深入研究IKAnalyzer3.2.8的源码,开发者可以了解中文分词的基本原理,学习如何优化分词算法,以及如何在实际项目中与搜索引擎框架集成,提升信息检索系统的效能。同时,源码阅读也是提升Java编程技能和理解软件...
**IKAnalyzer分词源码详解** IKAnalyzer是一款广泛使用的开源中文分词器,它主要针对Java平台设计,具有良好的性能和扩展性。该分词器适用于各种文本处理场景,如搜索引擎构建、信息检索、文本挖掘等。其核心在于...
**IKAnalyzer源码详解** **一、IKAnalyzer简介** IKAnalyzer是基于Java语言开发的一款开源的中文分词组件,主要用于解决在信息检索、文本挖掘等领域中的中文处理问题。它的全称为"Intelligent Chinese Analyzer ...
通过对IKAnalyzer源码的分析,我们可以看到其设计精妙之处在于利用了Trie树这一高效的数据结构来加速词典的加载和词汇的匹配。此外,自定义词典的功能也极大地增强了其灵活性和适应性。在实际应用中,理解IKAnalyzer...
**IKAnalyzer 2012源码分析** IKAnalyzer 是一个开源的、基于Java实现的中文分词器,主要用于提高中文信息处理的效率。这款工具广泛应用于搜索引擎、文本挖掘、信息检索等领域,其核心功能是对中文文本进行有效的...
从 2006年 12 月推出 1.0 版开始, IKAnalyzer 已经推出了 4 个大版本。最初,它是以开源项目Luence 为应用主体的,结合词典分词和文法分析算法的中文分词组件。 从 3.0 版本开始,IK 发展为面向 Java 的公用分词...
IK Analyzer 是一个开源的、基于Java实现的中文分词器,专为全文检索或信息提取等任务设计。它由尹力(Wu Li)在2006年发起,最初是为了改善Lucene的中文处理能力。自那时起,IK Analyzer已经发展成为一个广泛使用的...
IKAnalyzer2012_u6中文分词器以及手册正式版 Mode LastWriteTime Length Name ---- ------------- ------ ---- d----- 2017/10/29 1:41 doc -a---- 2017/10/29 1:41 414 IKAnalyzer.cfg.xml -a---- 2017/10/29 1...
源码包中包含的主要文件夹"j_IKAnalyzer-Stable_src"可能包含了以下关键组成部分: 1. **源代码**:这里是IKAnalyzer的核心代码,包括分词算法、词典管理、扩展机制等。通过阅读源码,开发者可以深入了解分词器的...
IKAnalyzer非常易用的java分词工具。可以自定义扩展词汇。 这个是一个完整的java项目demo。直接可以用,不用再去google下载了。添加了几个自定义词汇,测试好用。 运行ika.java里的main方法即可
ikanalyzer-2012_u6中文分词器,Maven配置 清除IKAnalyzer2012_u6所依赖的Lucene4.7.2相关jar包 引入IKAnalyzer2012_u6所依赖的Lucene5.5.4相关jar包
3. 重新启动Solr,现在你的索引和查询操作都将使用IKAnalyzer进行中文分词。 总的来说,IKAnalyzer是一个强大的中文分词工具,通过源码我们可以深入了解其工作原理,而预编译的jar包则为我们提供了便捷的使用方式。...
几种分词工具都试过,对于中文来说的的话个人觉得IKAnalyzer 最好用,不同版本的IKAnalyzer jar包,包括IKAnalyzer6.5.0、IKAnalyzer5.0、IKAnalyzer2012,IKAnalyzer6.5.0可完美兼容Lucene6+,IKAnalyzer5.0兼容...
解决lucene4.0与IKAnalyzer的冲突。解决Exception in thread "main" java.lang.VerifyError: class org.wltea.analyzer.lucene.IKAnalyzer overrides final method ...本资源包含了IKAnalyzer2012_FF_hf1.jar及源码
IK 分词器源码 此版本IK Analyzer 2012FF版,支持4Lucene.0以上 , 适合大家学习,开发自己的分词器,或参与该项目开发
ikanalyzer2.0.2源码是针对中文分词任务设计的一个开源项目,它主要基于Java语言实现,广泛应用于搜索引擎、信息检索系统以及文本挖掘等领域。ikanalyzer的核心目标是为了解决中文在英文为主导的Lucene等全文检索...
对于熟悉Java的开发者,可以基于 IKAnalyzer 的源码进行定制化开发,例如添加新的分词策略、优化性能、集成到自己的系统中等。 7. **应用场景** - 搜索引擎:IKAnalyzer 可用于构建搜索引擎,提升搜索结果的...
- **IK Analyzer 2012FF_hf1_source.rar**:这是IKAnalyzer 2012版本的源码压缩包,开发人员可以深入研究其内部实现,了解分词算法和逻辑,也可以根据需要进行定制和二次开发。 - **IKAnalyzer2012_u6.zip**:这是...
IKAnalyzer是一款专为中文处理设计的开源分词器,它主要应用于搜索引擎、信息检索系统、文本挖掘等领域。这款工具能够高效地对中文文本进行分词,使得计算机可以更好地理解和处理中文信息。IKAnalyzer的名字来源于...