Java分词器比较(ansj、hanlp、ik)
Java分词器比较(ansj、hanlp、ik)
一、分词工具
分词准确性排行:hanlp > ansj > 结巴 > IK > Smart Chinese Analysis
二、优缺点
1.ansj
- 优点:
1. 提供多种分词方式
2. 可直接根据内部词库分出人名、机构等信息
3. 可构造多个词库,在分词时可动态选择所要使用的词库 - 缺点:
1. 自定义词典时,系统词典还是被优先使用,导致词性不是自定义词典中的词性
2. 多单词英文姓名无法分出
3. 对标hanlp词库略少,学习成本高 - 适用场景
1. 若不使用自定义分词,可直接使用ansj
2.hanlp
- 优点:
1. 自定义分词、词性方便
2. 可分出多单词的英文名称(词典数据可带空格)
3. 可动态增删词库,
4. 动态添加词典前五千万速度很快,5m左右 - 缺点:
1. 动态添加词典前五千万快的很,越往后越慢
2. 词典文件添加自定义词典速度略慢,添加100w需要2m30s - 适用场景:
1. 词典数量少于五千万,词典数据若包含空格,用hanlp比较合适
3.IKanalyzer
- 优点:
1. 开源、轻量、
2. 支持自定义词典和远程词典
3. bug少,稳定,简单易用。 - 缺点:
1. 不够智能,
2. 词库需要自行维护
3. 不支持词性识别 - 适用场景:
1. 词典数量少于五千万,词典数据若包含空格,用hanlp比较合适
总结
- 对搜索要求不高的建议选用 IK 学习成本低,使用教程多,还支持远程词典
- Ansj和hanlp均基于自然处理语言,分词准确度高,活跃度来讲hanlp略胜一筹
ansj的使用
1.maven引入ansj包
<ansj.version>5.0.4</ansj.version>
<tree_split.version>1.5</tree_split.version>
<nlp-lang.version>1.7.7</nlp-lang.version>
<!-- ansj_seg -->
<dependency>
<groupId>org.ansj</groupId>
<artifactId>ansj_seg</artifactId>
<version>${ansj.version}</version>
</dependency>
<!-- tree_split -->
<dependency>
<groupId>org.ansj</groupId>
<artifactId>tree_split</artifactId>
<version>${tree_split.version}</version>
</dependency>
<!-- nlp-lang -->
<dependency>
<groupId>org.nlpcn</groupId>
<artifactId>nlp-lang</artifactId>
<version>${nlp-lang.version}</version>
</dependency>
- 2.在项目根目录下创建library文件夹,文件夹下包括以下几个词典文件(自行添加)
ambiguity.dic
default.dic
userLibrary.dic
- 3.使用
package com.zhen.segment;
import java.io.InputStream;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.ansj.domain.Result;
import org.ansj.domain.Term;
import org.ansj.library.UserDefineLibrary;
import org.ansj.splitWord.analysis.BaseAnalysis;
import org.ansj.splitWord.analysis.NlpAnalysis;
import org.ansj.splitWord.analysis.ToAnalysis;
import org.nlpcn.commons.lang.tire.domain.Forest;
import org.nlpcn.commons.lang.tire.domain.Value;
import org.nlpcn.commons.lang.tire.library.Library;
/**
* @author FengZhen
* @date Jan 30, 2019
* ansj分词
*/
public class SegmentTest {
public static void main(String[] args) {
// dynamicWord();
// localDic();
// moreUserDic();
}
/**
* 多用户词典(新增、删除)
*/
public static void moreUserDic() {
// 多用户词典
String str = "神探夏洛克这部电影作者.是一个dota迷";
System.out.println(ToAnalysis.parse(str));
// 两个词汇 神探夏洛克 douta迷
Forest dic1 = new Forest();
Library.insertWord(dic1, new Value("神探夏洛克", "define", "1000"));
Forest dic2 = new Forest();
Library.insertWord(dic2, new Value("dota迷", "define", "1000"));
System.out.println(ToAnalysis.parse(str, dic1, dic2));
System.out.println("-------删除dic1中的词");
Library.removeWord(dic1, "神探夏洛克");
System.out.println(ToAnalysis.parse(str, dic1, dic2));
}
/**
* 动态增删词库
*/
public static void dynamicWord(){
// 增加新词,中间按照'\t'隔开
UserDefineLibrary.insertWord("ansj中文分词", "userDefine", 1000);
Result result = ToAnalysis.parse("我觉得Ansj中文分词是一个不错的系统!我是王婆!");
System.out.println("增加新词例子:" + result);
// 删除词语,只能删除.用户自定义的词典.
UserDefineLibrary.removeWord("ansj中文分词");
result = ToAnalysis.parse("我觉得ansj中文分词是一个不错的系统!我是王婆!");
System.out.println("删除用户自定义词典例子:" + result);
//将用户自定义词典清空
UserDefineLibrary.clear();
}
/**
* 加载词典文件
*/
public static void localDic(){
try {
//读的是根目录下的
Forest rootForest = Library.makeForest("library/userLibrary.dic");
System.out.println(rootForest.toMap());
//加载字典文件,取的是resource下的
InputStream inputStream = SegmentTest.class.getResourceAsStream("/library/userLibrary.dic");
Forest resoutceForest=Library.makeForest(inputStream);
String str = "我觉得ansj中文分词是一个不错的系统!我是王婆!";
Result result=ToAnalysis.parse(str, resoutceForest);//传入forest
List<Term> termList=result.getTerms();
for(Term term:termList){
System.out.println(term.getName()+":"+term.getNatureStr());
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 基本分词
* 基本就是保证了最基本的分词.词语颗粒度最非常小的..所涉及到的词大约是10万左右.
* 基本分词速度非常快.在macAir上.能到每秒300w字每秒.同时准确率也很高.但是对于新词他的功能十分有限
* @param content
*/
public static void baseAnay(String content) {
Result result = BaseAnalysis.parse(delHTMLTag(content).replace("\n","").replace(" ", "").replace("\t",""));
System.out.println("result:" + result);
}
/**
* 精准分词
* 它在易用性,稳定性.准确性.以及分词效率上.都取得了一个不错的平衡.
* @param content
*/
public static void toAnay(String content){
Result result = ToAnalysis.parse(content);
System.out.println("result:" + result);
}
/**
* nlp分词(单条新闻处理7秒)
* 可以识别出未登录词.但是它也有它的缺点.速度比较慢.稳定性差.ps:我这里说的慢仅仅是和自己的其他方式比较.应该是40w字每秒的速度吧.
* 个人觉得nlp的适用方式.1.语法实体名抽取.未登录词整理.只要是对文本进行发现分析等工作
* 会把企业分开
* @param content
*/
public static void nlpAnay(String content){
Result result = NlpAnalysis.parse(delHTMLTag(content).replace("\n","").replace(" ", "").replace("\t",""));
System.out.println("result:" + result);
List<Term> terms = result.getTerms();
for (Term term : terms) {
String name = term.getName();
String nature = term.getNatureStr();
if (nature.equals("nt") || nature.equals("nr")) {
System.out.println("------------------");
System.out.println("getName:" + term.getName());
System.out.println("getNatureStr:" + term.getNatureStr());
}
}
}
/**
* 筛除HTML标签
* @param htmlStr
* @return
*/
public static String delHTMLTag(String htmlStr){
String regEx_script="<script[^>]*?>[\\s\\S]*?<\\/script>"; //定义script的正则表达式
String regEx_style="<style[^>]*?>[\\s\\S]*?<\\/style>"; //定义style的正则表达式
String regEx_html="<[^>]+>"; //定义HTML标签的正则表达式
Pattern p_script=Pattern.compile(regEx_script,Pattern.CASE_INSENSITIVE);
Matcher m_script=p_script.matcher(htmlStr);
htmlStr=m_script.replaceAll(""); //过滤script标签
Pattern p_style=Pattern.compile(regEx_style,Pattern.CASE_INSENSITIVE);
Matcher m_style=p_style.matcher(htmlStr);
htmlStr=m_style.replaceAll(""); //过滤style标签
Pattern p_html=Pattern.compile(regEx_html,Pattern.CASE_INSENSITIVE);
Matcher m_html=p_html.matcher(htmlStr);
htmlStr=m_html.replaceAll(""); //过滤html标签
return htmlStr.trim(); //返回文本字符串
}
}
hanlp的使用
- 1.maven添加依赖
<dependency>
<groupId>com.hankcs</groupId>
<artifactId>hanlp</artifactId>
<version>portable-1.7.1</version>
</dependency>
- 2.动态添加词
/**
* 新增词
*/
public static void addWord(String word, NatureEnum nature) {
logger.info("==== addWord@word:{},nature:{},weight:{} ====", word, nature.getNature(), nature.getWeight());
if (!StringUtils.isBlank(word) && !word.equals("null")) {
//大小括号问题
if (word.contains("(") || word.contains(")")) {
CustomDictionary.insert(word.replace("(", "(").replace(")", ")"), nature.getNature());
}else if (word.contains("(") || word.contains(")")) {
CustomDictionary.insert(word.replace("(", "(").replace(")", ")"), nature.getNature());
}
CustomDictionary.insert(word, nature.getNature());
}else {
logger.warn("==== addWord@ word({})为空 ====", word);
}
}
- 3.动态删除词
/**
* 删除词
* @param forest
* @param word
*/
public static void deleteWord(String word) {
logger.info("==== deleteWord@word:{} ====", word);
if (!StringUtils.isBlank(word)) {
CustomDictionary.remove(word);
}else {
logger.warn("==== deleteWord@word为空({}) ====", word);
}
}
- 4.使用
/**
* 分词
* @param content
* @param forests
* ToAnalysis精准分词
* BaseAnalysis基础分词
*/
public static SegmentResult segment(String content) {
logger.info("==== segment@content:{} ====", content);
SegmentResult segmentResult = new SegmentResult();
List<Term> terms = HanLP.segment(content);
Set<String> companySet = new HashSet<String>();
for (Term term : terms) {
String name = term.word;
String nature = term.nature.toString();
if (nature.equals(NatureEnum.Company.getNature())) {
companySet.add(name);
}
}
segmentResult.setCompanys(new ArrayList<String>(companySet));
logger.info("==== segment@分词结果:{},提取结果:{} ====", terms.toString(), segmentResult.toString());
return segmentResult;
}
- 5.自定义词典文件
词典文件格式如下,依次是 词、词性、权重
word nature weight
需要下载hanlp的data及hanlp.properties
https://github.com/hankcs/HanLP
data文件夹如下
ik分词器
- 依赖
<!-- ikanalyzer 中文分词器 -->
<dependency>
<groupId>com.janeluo</groupId>
<artifactId>ikanalyzer</artifactId>
<version>2012_u6</version>
<exclusions>
<exclusion>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-core</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-queryparser</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-analyzers-common</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- lucene-queryparser 查询分析器模块 -->
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-queryparser</artifactId>
<version>7.3.0</version>
</dependency>
- 使用
/**
* 分词器(可能比较耗时)
*
* @param content str
* @return java.util.List<java.lang.String>
* @author ext.wenzhongming1
* @since 2023/2/28 14:18
*/
public static List<String> iKSegToList(String content) {
content = clearHtmlTag(content);
List<String> list = new ArrayList<>();
try {
if (StringUtils.isEmpty(content)) {
return Lists.newArrayList();
}
StringReader sr = new StringReader(content);
// 关闭智能分词 (对分词的精度影响较大)
IKSegmenter ik = new IKSegmenter(sr, false);
Lexeme lex;
while ((lex = ik.next()) != null) {
String lexemeText = lex.getLexemeText();
list.add(lexemeText);
}
} catch (Exception e) {
log.error("分词异常");
}
return list;
}
public static void main(String[] args) {
// ik 切词
String content = "https://www.relaxheart.cn 是xx同学的个人兴趣分享网站";
System.out.println(iKSegToList(content));
}
- 输出
加载扩展词典:extend.dic
加载扩展停止词典:stopword.dic
[https, www.relaxheart.cn, www, relaxheart, cn, 是, xx, 同学, 个人兴趣, 个人, 兴趣, 分享, 网站]