ansj第一步会进行原子切分和全切分,并且是在同时进行的。所谓原子,是指短句中不可分割的最小语素单位。例如,一个汉字就是一个原子。
全切分,就是把一句话中的所有词都找出来,只要是字典中有的就找出来。例如,“提高中国人生活水平”包含的词有:提高、高中、中国、国人、人生、生活、活水、水平。接着以“提高中国人生活水平”为例,调用ansj标准分词:

String str = "提高中国人生活水平" ;
Result result = ToAnalysis.parse(str);
System.out.println(result.getTerms());

Analysis类的analysisStr(String temp)会对几句话进行分词。先不考虑用户自定义词典,直接看这两几代码:

if (startOffe < gp.chars.length ) {
analysis(gp, startOffe, gp.chars.length);
}

其中,terms[0]是“提”,terms[0].next是“提高”。由于“提高中”不再是个词,所以terms[0].next.next是null。类似的,terms[1]是“高”,terms[1].next是“高中”,terms[1].next.next是null。
至于terms[9]为什么是null,这是因为“水平”是个词,但可以继续,比如“水平面”、“水平线”;而且,“平”也可以继续,比如“评价”、“平凡”。如果把例句换成“提高中国人民生活水平啊”,就不会出现null。这里先不做深入讨论。
    看着一行行代码,挺多挺复杂的。真正debug一遍,发现很多代码都执行不到。看来有大量的代码,是用来处理少数特殊情况的。涉及到的几个类及基本介绍(只看与本节内容相关的属性和方法,不然太多了):

1. Analysis
基本分词+人名识别的一个抽象类。
  (1) analysis(Graph gp, int startOffe, int endOffe)
该方法用于对一句话进行分词。
对于switch语句switch (status(chars[i])),
case 4:英文字母
case 5:阿拉伯数字或者小数点
以上两种情况,处理逻辑都比较简单,重头戏是default。
在default中,start是本轮分词的起始位置,end是本轮分词的终止位置。start和end之间只能是汉子或者标点符号。先下面这几行核心代码:

gwi.setChars(chars, start, end);
while ((str = gwi.allWords()) != null) {
Term term = new Term(str, gwi.offe, gwi.getItem());
gp.addTerm(term);
}

这几行代码就实现了将一句汉语,一个一个地分词。每分出一个词,就实例化一个Term,并加入到图(也就是变量gp)中。实例化Term的参数,str是该词的汉字表示;gwi.offe是该词在句子中起始位置的偏移量(这个参数很重要,保证了新的Term可以被插入正确的位置);gwi.getItem()是该词在字典中的一些信息。ansj的早期版本,只有上面这几行代码。目前的版本(5.1.2)多了下面这几行代码:

int len = term.getOffe() - max;
if (len > ) {
for (; max < term.getOffe();) {
gp.addTerm(new Term(String.valueOf(chars[max]), max, TermNatures.NULL));
max++;
}
}

这是为了强行将不能为词的单字,插入到terms。我们可以把上面几行代码注释,然后以“深圳市碧荔花园”为例进行切分,analysis处理后结果如下:

注意上图中,terms[7]是null。正常情况下,terms[7]应该是荔。荔在核心字典中的信息如下:
33620 荔 122986 -1 1 null
state是1,也就是说,"荔"不能单字为词(比如可以组成"荔枝"这个词)。但是"碧荔花园"是个小区名,"荔"不能为词,"荔花"根本就不是个词。这会导致while ((str = gwi.allWords()) != null)这里获取分出的词时,直接跳过“荔”。
上面列出的那几行代码,就是为了解决这种歌特殊情况,解决terms[7]是null的问题。而在后面这段代码:

int len = end - max;
if (len > ) {
for (; max < end;) {
gp.addTerm(new Term(String.valueOf(chars[max]), max, TermNatures.NULL));
max++;
}
}

解决的是“荔”这种不能为词的单字,位于句尾的情况。例如“深圳市碧荔花园荔荔荔荔荔”这句话。这印证了我上面说过的那句话吧,有大量的代码,是用来处理少数特殊情况的。
2 GetWordsImpl
该类用于从核心字典(core.dic)中获取词语。
(1)chars
该属性是一个char型数组,存储了待分词的句子,如下所示:

2 GetWordsImpl
该类用于从核心字典(core.dic)中获取词语。
(1) chars
该属性是一个char型数组,存储了待分词的句子,如下所示:

(2) offe
该属性表示当前词起始位置的偏移量,是public类型的,可用于外部访问。
例如“深圳市人民政府。”这句话,“深”、“深圳”、“深圳市”三个词的offe都是0。
与offe对于的,还有可以private类型的start,也是当前词起始位置的偏移量。当一个词语结束时,start会比offe多1。
(3) getStatement()
实现了对双数组前缀树的查询。查询某字或词在核心字典(core.dic)中的状态。
0 代表这个字不在词典中。
1 代表这还不是个词,需要继续。例如:102029 如日中 79205 140442 1 null
2 表示这是个词,但是还可以继续。例如:96274 囫囵 74746 22251 2 {d=0}
3 表示这已经是个词了,后面不能继续了。例如:102819 姗姗来迟 65536 102815 3 {i=2}
其中,标点符号的状态也是3。
(4) allWords()
根据待分词的句子(也就是上面提到的chars属性),一个一个地返回分出的词语。
for (; i < charsLength; i++)这个for循环的i是这个类的属性,并不是一个临时变量,从而实现一个一个地返回分出的词语。
注意这个switch语句:switch (getStatement())
case 0:表示字典中没有这个词。这有两种情况:
1. 这是个单字,直接返回这个单子即可,从下一个位置为起点继续分词。
2. 这不是个单子,例如“人生活”这个词,在字典中是没有的。这时什么也不返回,从下一个位置为起点去分词。
至于遇到“如日中”这种词,getStatement()返回的是1,switch语句不对这种情况做任何处理,需要接着向后查找。
3. Graph
该类实现了一个图(大学时没好好学图论,没想到应用在这里的)

转自: https://www.cnblogs.com/royhoo/p/6642141.html

ansj分词原理的更多相关文章

  1. 在Solr中配置和使用ansj分词

    在上一节[编译Ansj之Solr插件]中介绍如何编译ansj分词在solr(lucene)环境中使用的接口,本章将介绍如何在solr中使用ansj,其步骤主要包括:下载或者编译ansj和nlp-lan ...

  2. Ansj分词双数组Trie树实现与arrays.dic词典格式

    http://www.hankcs.com/nlp/ansj-word-pairs-array-tire-tree-achieved-with-arrays-dic-dictionary-format ...

  3. ansj分词

    本文转载至:https://blog.csdn.net/bitcarmanlee/article/details/53607776 最近的项目需要使用到分词技术.本着不重复造轮子的原则,使用了ansj ...

  4. elasticsearch安装ansj分词器

    1.概述    elasticsearch用于搜索引擎,需要设置一些分词器来优化索引.常用的有ik_max_word: 会将文本做最细粒度的拆分.ik_smart: 会做最粗粒度的拆分.ansj等. ...

  5. ansj分词史上最详细教程

    最近的项目需要使用到分词技术.本着不重复造轮子的原则,使用了ansj_seg来进行分词.本文结合博主使用经过,教大家用最快的速度上手使用ansj分词. 1.给ansj来个硬广 项目的github地址: ...

  6. elasticsearch使用ansj分词器

    目前elasticsearch的版本已经更新到7.0以上了,不过由于客户需要5.2.2版本的elasticsearch,所以还是需要安装的,并且安装上ansj分词器.在部署ES的时候,采用容器的方式进 ...

  7. ansj分词器使用记录

    //最简单实例 String ruiec = “分词测试123456100名”; //剔除指定的分词 s.insertStopWords("100名"); //剔除标点符号(w) ...

  8. Jieba分词原理与解析

    https://www.jianshu.com/p/dfdfeaa7d01f 1 HMM模型   image.png 马尔科夫过程:   image.png   image.png 以天气判断为例:引 ...

  9. jieba分词原理-DAG(NO HMM)

    最近公司在做一个推荐系统,让我给论坛上的帖子找关键字,当时给我说让我用jieba分词,我周末回去看了看,感觉不错,还学习了一下具体的原理 首先,通过正则表达式,将文章内容切分,形成一个句子数组,这个比 ...

随机推荐

  1. vue_axios请求封装、异常拦截统一处理

    1.前端网络请求封装.异常统一处理 vue中采用axios处理网络请求,避免请求接口重复代码,以及各种网络情况造成的异常情况的判断,采用axios请求封装和异常拦截操作: axios 请求封装 // ...

  2. .Net AppDomain详解(二)

    AppDomain 类 表示应用程序域,它是一个应用程序在其中执行的独立环境. 此类不能被继承. 命名空间:   System程序集:  mscorlib(位于 mscorlib.dll) 继承层次结 ...

  3. 【OpenCV】选择ROI区域 (转)

    问题描述:在测试目标跟踪算法时,需要选择不同区域作为目标,进行目标跟踪,测试目标跟踪的效果. 解决思路: 1.OpenCV中提供了鼠标交互控制,利用setMouseCallback()给固定的窗口设置 ...

  4. FTP上传、下载(简单小例子)

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.I ...

  5. 解决ScrollView嵌套RecyclerView的显示及滑动问题

        项目中时常需要实现在ScrollView中嵌入一个或多个RecyclerView.这一做法通常会导致如下几个问题 页面滑动卡顿 ScrollView高度显示不正常 RecyclerView内容 ...

  6. ASP.NET Web API(MVC API)

    ASP.NET Web API是​​一个框架,可以很容易构建达成了广泛的HTTP服务客户端,包括浏览器和移动设备.是构建RESTful应用程序的理想平台的.NET框架. 上面是微软对Web API给出 ...

  7. 基于Tcp协议的简单Socket通信实例(JAVA)

    好久没写博客了,前段时间忙于做项目,耽误了些时间,今天开始继续写起~ 今天来讲下关于Socket通信的简单应用,关于什么是Socket以及一些网络编程的基础,这里就不提了,只记录最简单易懂实用的东西. ...

  8. 物联网架构成长之路(8)-EMQ-Hook了解、连接Kafka发送消息

    1. 前言 按照我自己设计的物联网框架,对于MQTT集群中的所有消息,是要持久化到磁盘的,这里采用一个消息队列中间件Kafka作为数据缓冲,缓冲结果存到数据仓库中,以供后续作为数据分析.由于MQTT集 ...

  9. CentOS 上开启 BBR 加速

    BBR 算法需要 Linux 4.9 及以上的内核支持,所以想要使用该方式的需要先升级内核版本. 在 Cent OS 7 上的 Linux 内核是 3.10, 使用 uname -r 查看内核版本 [ ...

  10. java中的数据加密3 非对称加密

    非对称加密也加公钥加密,不对称算法使用一对密钥对,一个公钥,一个私钥,使用公钥加密的数据,只有私钥能解开(可用于加密):同时,使用私钥加密的数据,只有公钥能解开(签名).但是速度很慢(比私钥加密慢10 ...