hanlp中文分词器解读_计算机软件及应用_it计算机_专业资料
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
中文分词器解析hanlp分词器接口设计:
提供外部接口:
分词器封装为静态工具类,并提供了简单的接口
标准分词是最常用的分词器,基于HMM-Viterbi实现,开启了中国人名识别和音译人名识别,调用方法如下:
HanLP.segment其实是对StandardTokenizer.segment的包装。
/**
* 分词
*
* @param text 文本
* @return切分后的单词
*/
publicstatic List<Term>segment(String text)
{
return StandardTokenizer.segment(text.toCharArray());
}
/**
* 创建一个分词器<br>
* 这是一个工厂方法<br>
* 与直接new一个分词器相比,使用本方法的好处是,以后HanLP升级了,总能用上最合适的分词器
* @return一个分词器
*/
publicstatic Segment newSegment()
}
publicclass StandardTokenizer
{
/**
* 预置分词器
*/
publicstaticfinalSegment SEGMENT = HanLP.newSegment();
/**
* 分词
* @param text 文本
* @return分词结果
*/
publicstatic List<Term>segment(String text)
{
return SEGMENT.seg(text.toCharArray());
}
/**
* 分词
* @param text 文本
* @return分词结果
*/
publicstatic List<Term>segment(char[]text)
{
return SEGMENT.seg(text);
}
/**
* 切分为句子形式
* @param text 文本
* @return句子列表
*/
publicstatic List<List<Term>>seg2sentence(String text)
{
return SEGMENT.seg2sentence(text);
}
}
publicstatic Segment newSegment()
{
returnnew ViterbiSegment();// Viterbi分词器是目前效率和效果的最佳平衡
}
/**
* Viterbi分词器<br>
* 也是最短路分词,最短路求解采用Viterbi算法
*
* @author hankcs
*/
publicclass ViterbiSegment extends WordBasedGenerativeModelSegment
NLP分词NLPTokenizer会执行全部命名实体识别和词性标注。
,调用方法如下:
∙NLP分词NLPTokenizer会执行全部命名实体识别和词性标注。
∙所以速度比标准分词慢,并且有误识别的情况。
publicclass NLPTokenizer
{
/**
* 预置分词器
*/
publicstaticfinalSegment SEGMENT =
HanLP.newSegment().enableNameRecognize(true).enableTranslatedNameRecognize( true).enableJapaneseNameRecognize(true).enablePlaceRecognize(true).enableOr ganizationRecognize(true).enablePartOfSpeechTagging(true);
publicstatic List<Term>segment(String text)
{
return SEGMENT.seg(text);
}
/**
* 分词
* @param text 文本
* @return分词结果
*/
publicstatic List<Term>segment(char[]text)
{
return SEGMENT.seg(text);
}
/**
* 切分为句子形式
* @param text 文本
* @return句子列表
*/
publicstatic List<List<Term>>seg2sentence(String text)
{
return SEGMENT.seg2sentence(text);
}
}
索引分词IndexTokenizer是面向搜索引擎的分词器,能够对长词全切分,另外通过term.offset可以获取单词在文本中的偏移量。
调用方法如下:
List<Term>termList=IndexTokenizer.segment("主副食品");
for(Termterm:termList)
{
System.out.println(term+"
["+term.offset+":"+(term.offset+term.word.length())+"]");
}
publicclass IndexTokenizer
{
/**
* 预置分词器
*/
publicstaticfinalSegment SEGMENT = HanLP.newSegment().enableIndexMode(true); publicstatic List<Term>segment(String text)
{
return SEGMENT.seg(text);
}
/**
* 分词
* @param text 文本
* @return分词结果
*/
publicstatic List<Term>segment(char[]text)
{
return SEGMENT.seg(text);
}
/**
* 切分为句子形式
* @param text 文本
* @return句子列表
*/
publicstatic List<List<Term>>seg2sentence(String text)
{
return SEGMENT.seg2sentence(text);
}
}
繁体分词TraditionalChineseTokenizer可以直接对繁体进行分词,输出切分后的繁体词语。
调用方法如下:
List<Term>termList=TraditionalChineseTokenizer.segment("大衛貝克漢不僅僅是名著名球員,球場以外,其妻為前辣妹合唱團成員維多利亞·碧咸,亦由於他擁有突出外表、百變髮型及正面的形象,以至自己品牌的男士香水等商品,及長期擔任運動品牌Adidas的代言人,因此對大眾傳播媒介和時尚界等方面都具很大的影響力,在足球圈外所獲得的認受程度可謂前所未見。
");
System.out.println(termList);
* 繁体中文分词器
*
* @author hankcs
*/
publicclass TraditionalChineseTokenizer
{
/**
* 预置分词器
*/
publicstaticSegment SEGMENT = HanLP.newSegment();
privatestatic List<Term>segSentence(String text)
{
if(text.length()==0)return Collections.emptyList();
LinkedList<ResultTerm<String>>tsList=CommonAhoCorasickSegmentUtil.segment(t ext,TraditionalChineseDictionary.trie);
StringBuilder sbSimplifiedChinese=new StringBuilder(text.length());
boolean equal=true;
for(ResultTerm<String>term:tsList)
{
if(bel==null)bel=term.word;
elseif(bel.length()!=term.word.length())equal=false; sbSimplifiedChinese.append(bel);
}
String simplifiedChinese=sbSimplifiedChinese.toString();
List<Term>termList=SEGMENT.seg(simplifiedChinese);
if(equal)
{
int offset=0;
for(Term term:termList)
{
term.word=text.substring(offset,offset+term.length());
term.offset=offset;
offset+=term.length();
}
}
else
{
Iterator<Term>termIterator=termList.iterator();
Iterator<ResultTerm<String>>tsIterator=tsList.iterator();
ResultTerm<String>tsTerm=tsIterator.next();
int offset=0;
while(termIterator.hasNext())
Term term=termIterator.next();
term.offset=offset;
if(offset>tsTerm.offset+tsTerm.word.length())tsTerm=tsIterator.next();
if(offset==tsTerm.offset&&term.length()==bel.length())
{
term.word=tsTerm.word;
}
else term.word=SimplifiedChineseDictionary.convertToTraditionalChinese(term. word);
offset+=term.length();
}
}
return termList;
}
publicstatic List<Term>segment(String text)
{
List<Term>termList=new LinkedList<Term>();
for(String sentence:SentencesUtil.toSentenceList(text))
{
termList.addAll(segSentence(sentence));
}
return termList;
}
/**
* 分词
*
* @param text 文本
* @return分词结果
*/
publicstatic List<Term>segment(char[]text)
{
return segment(CharTable.convert(text));
}
/**
* 切分为句子形式
*
* @param text 文本
* @return句子列表
*/
publicstatic List<List<Term>>seg2sentence(String text)
{
List<List<Term>>resultList=new LinkedList<List<Term>>();
{
for(String sentence:SentencesUtil.toSentenceList(text))
{
resultList.add(segment(sentence));
}
}
return resultList;
}
}
极速分词是词典最长分词,速度极其快,精度一般。
调用方法如下:
String text ="江西鄱阳湖干枯,中国最大淡水湖变成大草原";
System.out.println(SpeedTokenizer.segment(text));
long start = System.currentTimeMillis();
int pressure =1000000;
for(int i =0; i < pressure;++i)
{
SpeedTokenizer.segment(text);
}
double costTime =(System.currentTimeMillis()- start)/(double)1000; System.out.printf("分词速度:%.2f字每秒", text.length()* pressure / costTime);
∙在i7上跑出了2000万字每秒的速度。
∙使用的算法是《Aho Corasick自动机结合DoubleArrayTrie极速多模式匹配》
/**
* 极速分词,基于Double Array Trie实现的词典分词,适用于“高吞吐量”“精度一般”的场合
* @author hankcs
*/
publicclass SpeedTokenizer
{
/**
* 预置分词器
*/
publicstaticfinalSegment SEGMENT = new DoubleArrayTrieSegment(); publicstatic List<Term>segment(String text)
{
return SEGMENT.seg(text.toCharArray());
}
/**
* 分词
* @param text 文本
* @return分词结果
*/
publicstatic List<Term>segment(char[]text)
{
return SEGMENT.seg(text);
}
/**
* 切分为句子形式
* @param text 文本
* @return句子列表
*/
publicstatic List<List<Term>>seg2sentence(String text)
{
return SEGMENT.seg2sentence(text);
}
}
接下来介绍的分词器是由用户动态创建,使用场景不常见的分词器。
N最短路分词器NShortSegment比最短路分词器( DijkstraSegment )慢,但是效果稍微好一些,对命名实体识别能力更强。
调用方法如下:
Segment nShortSegment =new
NShortSegment().enableCustomDictionary(false).enablePlaceRecognize(tr ue).enableOrganizationRecognize(true);
Segment shortestSegment =new
ViterbiSegment().enableCustomDictionary(false).enablePlaceRecognize(t rue).enableOrganizationRecognize(true);
String[] testCase =new String[]{
"刘喜杰石国祥会见吴亚琴先进事迹报告团成员",
};
for(String sentence : testCase)
{
System.out.println("N-最短分词:"+nShortSegment.seg(sentence)+"\n 最短路分词:"+ shortestSegment.seg(sentence));
}
一般场景下最短路分词的精度已经足够,而且速度比N最短路分词器快几倍,请酌情选择。
/**
* N最短分词器
*
* @author hankcs
*/
publicclass NShortSegment extends WordBasedGenerativeModelSegment /**
* 最短路径分词
* @author hankcs
*/
publicclass DijkstraSegment extends WordBasedGenerativeModelSegment {
基于CRF模型和BEMS标注训练得到的分词器。
调用方法如下:
Segmentsegment=new CRFSegment();
segment.enablePartOfSpeechTagging(true);
List<Term>termList=segment.seg("你看过穆赫兰道吗");
System.out.println(termList);
for(Termterm:termList)
{
if(term.nature==null)
{
System.out.println("识别到新词:"+term.word);
}
}
publicclass CRFSegment extends CharacterBasedGenerativeModelSegment
在上面的例子中,一些工具类包装了配置好的分词器。
HanLP同时支持用户动态创建分词器和配置分词器。
既可以用new创建,也可以用工具类创建,推荐后者,可以应对未来的版本升级。
分词器是Java对象,可以用传统的new关键字创建任意的分词器。
也是最短路分词,最短路求解采用Viterbi算法:
依然是最短路分词,最短路求解采用Dijkstra算法:
∙DijkstraSegment比ViterbiSegment慢一点,但是调试信息更加丰富。
N最短分词器:
∙算法很美,速度很慢。
使用AhoCorasickDoubleArrayTrie实现的最长分词器:
∙应该是速度最快的词典分词了。
基于CRF的分词器:
∙应用场景不多。
通过此工厂方法得到的是当前版本速度和效果最平衡的分词器:
推荐用户始终通过工具类HanLP调用,这么做的好处是,将来HanLP升级后,用户无需修改调用代码。
所有分词器都是Segment的子类,Segment提供以下配置接口:
/**
* 设为索引模式
*
* @return
*/
public Segment enableIndexMode(boolean enable)
/**
* 开启词性标注
* @param enable
* @return
*/
public Segment enablePartOfSpeechTagging(boolean enable)
/**
* 开启人名识别
* @param enable
* @return
*/
public Segment enableNameRecognize(boolean enable)
/**
* 开启地名识别
* @param enable
* @return
*/
public Segment enablePlaceRecognize(boolean enable)
/**
* 开启机构名识别
* @param enable
* @return
*/
public Segment enableOrganizationRecognize(boolean enable)
/**
* 是否启用用户词典
*
* @param enable
*/
public Segment enableCustomDictionary(boolean enable)
/**
* 是否启用音译人名识别
*
* @param enable
*/
public Segment enableTranslatedNameRecognize(boolean enable)
/**
* 是否启用日本人名识别
*
* @param enable
*/
public Segment enableJapaneseNameRecognize(boolean enable)
/**
* 是否启用偏移量计算(开启后Term.offset才会被计算)
* @param enable
* @return
*/
public Segment enableOffset(boolean enable)
/**
* 是否启用所有的命名实体识别
* @param enable
* @return
*/
public Segment enableAllNamedEntityRecognize(boolean enable)
用户可以使用链式语法对Segment执行创建和配置操作,一气呵成:
对于工具类中的分词器,也可以使用暴露出来的SEGMENT成员对其进行配置:
Stringtext="泽田依子是上外日本文化经济学院的外教";
System.out.println(StandardTokenizer.segment(text)); StandardTokenizer.SEGMENT.enableAllNamedEntityRecognize(true); System.out.println(StandardTokenizer.segment(text));
工具类:
字符集识别工具类
字节转换工具类
词典与词语和词性相关的工具类
计算权值工具类
预定义
分割成句子工具类
文本工具类
层叠马尔科夫分词-张华平分词
张华平分词流程图
代码详解:
主要思想
该分词系统的主要是思想是先通过CHMM(层叠形马尔可夫模型)进行分词,通过分层,既增加了分词的准确性,又保证了分词的效率.共分五层,如下图所示:
实现思路
基本思路:先进行原子切分,然后在此基础上进行N-最短路径粗切分,找出前N个最符合的切分结果,生成二元分词表,然后生成分词结果,接着进行词性标注并完成主要分词步骤.
Java 代码梳理
Segsentence函数:
Segsentence 流程:
public List<Term>segSentence(char[] sentence)
{
WordNetwordNetOptimum = new WordNet(sentence);
WordNetwordNetAll = new WordNet(sentence);
// char[] charArray = text.toCharArray();
// Nshort生成粗分结果
List<List<Vertex>>coarseResult = BiSegment(sentence, 2, wordNetOptimum, wordNetAll);
booleanNERexists = false;
for (List<Vertex>vertexList : coarseResult)
{
if (HanLP.Config.DEBUG)
{
System.out.println("粗分结果" + convert(vertexList, false));
}
// 实体命名识别
if (config.ner)
{
wordNetOptimum.addAll(vertexList);
intpreSize = wordNetOptimum.size();
if (Recognize)
{
PersonRecognition.Recognition(vertexList, wordNetOptimum, wordNetAll);
}
if (config.translatedNameRecognize)
{
TranslatedPersonRecognition.Recognition(vertexList, wordNetOptimum, wordNetAll);
}
if (config.japaneseNameRecognize)
{
JapanesePersonRecognition.Recognition(vertexList, wordNetOptimum, wordNetAll);
}
if (config.placeRecognize)
{
PlaceRecognition.Recognition(vertexList, wordNetOptimum, wordNetAll);
}
if (anizationRecognize)
{
// 层叠隐马模型——生成输出作为下一级隐马输入
vertexList =
pute(GenerateBiGraph(wordNetOptimum));
wordNetOptimum.addAll(vertexList); OrganizationRecognition.Recognition(vertexList, wordNetOptimum, wordNetAll); }
if (!NERexists && preSize != wordNetOptimum.size())
{
NERexists = true;
}
}
}
List<Vertex>vertexList = coarseResult.get(0);
if (NERexists)
{
//命名实体识别后生成更新的词网
Graphgraph = GenerateBiGraph(wordNetOptimum);
vertexList = pute(graph);
if (HanLP.Config.DEBUG)
{
System.out.printf("细分词网:\n%s\n", wordNetOptimum);
System.out.printf("细分词图:%s\n", graph.printByTo());
}
}
// 数字识别
if (config.numberQuantifierRecognize)
{
mergeNumberQuantifier(vertexList, wordNetAll, config); }
// 如果是索引模式则全切分
if (config.indexMode)
{
return decorateResultForIndexMode(vertexList, wordNetAll);
}
// 是否标注词性
if (config.speechTagging)
{
speechTagging(vertexList);
}
return convert(vertexList, config.offset);
}
}
C++ 代码流程梳理
//Paragraph Segment and POS Tagging
bool CResult::ParagraphProcessing(char *sParagraph,char *sResult)
{
........
Processing(sSentence,1); //Processing and output the result of current sentence. Output(m_pResult[0],sSentenceResult,bFirstIgnore); //Output to the im ediate result
.......
}
3.主要的分词处理是在Processing()方法里面发生的,下面我们对它进行进一步的分析.
bool CResult::Processing(char *sSentence,unsigned int nCount)
{
......
//进行二叉分词
m_Seg.BiSegment(sSentence,
m_dSmoothingPara,m_dictCore,m_dictBigram,nCount);
......
//在此处进行词性标注
m_POSTagger.POSTagging(m_Seg.m_pWordSeg[nIndex],m_dictCore,m_dictCore );
......
}
原子切分:
根据分隔符断句
ICTCLAS分词的第一步就是原子分词。
但在进行原子切分之前,首先要进行断句处理。
所谓断句,就是根据分隔符、回车换行符等语句的分隔标志,把源字符串分隔成多个稍微简单一点的短句,再进行分词处理,最后再把各个分词结果合起来,形成最终的分词结果。
//Paragraph Segment and POS Tagging
bool CResult::ParagraphProcessing(char *sParagraph,char *sResult)
{
char *sSentence,sChar[3];
char *sSentenceResult;
unsigned int nLen=strlen(sParagraph)+13;
sSentence=new char[nLen];//malloc buffer
sSentenceResult=new char[nLen*3];//malloc buffer
sSentence[0]=0;
unsigned int
nPosIndex=0,nParagraphLen=strlen(sParagraph),nSentenceIndex=0;
sChar[2]=0;
sResult[0]=0;//Init the result
bool bFirstIgnore=true;
strcpy(sSentence,SENTENCE_BEGIN);//Add a sentence begin flag int ntest=strlen(sSentence);
//把文章按照标点分隔符切割成一段段,进行处理
while(nPosIndex<nParagraphLen)
{//Find a whole sentence which separated by ! . \n \r
sChar[0]=sParagraph[nPosIndex];//Get a char
sChar[1]=0;
if(sParagraph[nPosIndex]<0)
{//double byte char
nPosIndex+=1;
sChar[1]=sParagraph[nPosIndex];
}
nPosIndex+=1;
/*
#define SEPERATOR_C_SENTENCE "。
!?:;…"
#define SEPERATOR_C_SUB_SENTENCE "、,()“”‘’"
#define SEPERATOR_E_SENTENCE "!?:;"
#define SEPERATOR_E_SUB_SENTENCE ",()\042'"
#define SEPERATOR_LINK "\n\r "
*/
分隔符断句
if(CC_Find(SEPERATOR_C_SENTENCE,sChar)||CC_Find(SEPERATOR_C_SUB_ SENTENCE,sChar)||strstr(SEPERATOR_E_SENTENCE,sChar)||strstr(SEPERATOR_E _SUB_SENTENCE,sChar)||strstr(SEPERATOR_LINK,sChar))
{//Reach end of a sentence.Get a whole sentence
if(!strstr(SEPERATOR_LINK,sChar))//Not link seperator
{
strcat(sSentence,sChar);
}
if(sSentence[0]!=0&&strc m p(sSentence,SENTENCE_BEGIN)!=0)
{
if(!strstr(SEPERATOR_C_SUB_SENTENCE,sChar)&&!strstr(SEPERATOR_E_SU B_SENTENCE,sChar))
strcat(sSentence,SENTENCE_END);//Add sentence ending flag
Processing(sSentence,1);//Processing and output the result of current sentence.
Output(m_pResult[0],sSentenceResult,bFirstIgnore);//Output to the im ediate result
//bFirstIgnore=t rue;
strcat(sResult,sSentenceResult);//Store in the result buffer }
if(strstr(SEPERATOR_LINK,sChar))//Link the result with the SEPERATOR_LINK
{
strcat(sResult,sChar);
strcpy(sSentence,SENTENCE_BEGIN);//Add a sentence begin flag
//sSentence[0]=0;//New sentence, and begin new segmentation
//bFirstIgnore=false;
}
else
if(strstr(SEPERATOR_C_SENTENCE,sChar)||strstr(SEPERATOR_E_SENTENCE,sCha r))
{
strcpy(sSentence,SENTENCE_BEGIN);//Add a sentence begin flag
//sSentence[0]=0;//New sentence, and begin new segmentation
//bFirstIgnore=false;
}
else
{
strcpy(sSentence,sChar);//reset current sentence, and add the previous end at begin position
}
}
else //Other chars and store in the sentence buffer
strcat(sSentence,sChar);
}
//特殊情况处理
if(sSentence[0]!=0&&strc m p(sSentence,SENTENCE_BEGIN)!=0)
{
strcat(sSentence,SENTENCE_END);//Add sentence ending flag
Processing(sSentence,1);//Processing and output the result of current sentence.
Output(m_pResult[0],sSentenceResult,bFirstIgnore);//Output to the imediate result
strcat(sResult,sSentenceResult);//Store in the result buffer
}
delete [] sSentence;//FREE sentence buffer
delete [] sSentenceResult;//free buffer
return true;
}
输出实例
"始##始即可进行原子分词," + ",所谓原子,"+ ",是指该短句中不可分割的最小语素单位。
末##末"
原子分词
分成短句之后,即可进行原子分词,所谓原子,是指该短句中不可分割的最小语素单位。
一个汉字、短句前后的开始结束标识字段、全角标点符号、连在一起的数字字母单字节字符等。
最后一种情况可以举例说明,比如:三星SHX-132型号的手机1元钱,则SHX-132、1都是一个原子,其它的每个汉字是一个原子。
与c++相似的函数实现,为了速度考虑,采用了上述快速原子切分
C++ 预定义
#define CT_SENTENCE_BEGIN 1//Sentence begin
#define CT_SENTENCE_END 4//Sentence ending
#define CT_SINGLE 5//SINGLE byte
#define CT_DELIMITER CT_SINGLE+1//delimiter
#define CT_CHINESE CT_SINGLE+2//Chinese Char
#define CT_LETTER CT_SINGLE+3//HanYu Pinyin
#define CT_NUM CT_SINGLE+4//HanYu Pinyin
#define CT_INDEX CT_SINGLE+5//HanYu Pinyin
#define CT_OTHER CT_SINGLE+12//Other
#define POSTFIX_SINGLE "坝邦堡杯城池村单岛道堤店洞渡队法峰府冈港阁宫沟国海号河湖环集江奖礁角街井郡坑口矿里岭楼路门盟庙弄牌派坡铺旗桥区渠泉人山省市水寺塔台滩坛堂厅亭屯湾文屋溪峡县线乡巷型洋窑营屿语园苑院闸寨站镇州庄族陂庵町"
#define POSTFIX_MUTIPLE {"半岛","草原","城市","大堤","大公国","大桥","地区","帝国","渡槽","港口","高速公路","高原","公路","公园","共和国","谷地","广场","国道","海峡","胡同","机场","集镇","教区","街道","口岸","码头","煤矿","牧场","农场","盆地","平原","丘陵","群岛","沙漠","沙洲","山脉","山丘","水库","隧道","特区","铁路","新村","雪峰","盐场","盐湖","渔场","直辖市","自治区","自治县","自治州",""}
#define TRANS_ENGLISH "·—阿埃艾爱安昂敖奥澳笆芭巴白拜班邦保堡鲍北贝本比毕彼别波玻博勃伯泊卜布才采仓查差柴彻川茨慈次达大戴代丹旦但当道德得的登迪狄蒂帝丁东杜敦多额俄厄鄂恩尔伐法范菲芬费佛夫福弗甫噶盖干冈哥戈革葛格各根古瓜哈海罕翰汗汉豪合河
赫亨侯呼胡华霍基吉及加贾坚简杰金京久居君喀卡凯坎康考柯科可克肯库奎拉喇莱来兰郎朗劳勒雷累楞黎理李里莉丽历利立力连廉良列烈林隆卢虏鲁路伦仑罗洛玛马买麦迈曼茅茂梅门蒙盟米蜜密敏明摩莫墨默姆木穆那娜纳乃奈南内尼年涅宁纽努诺欧帕潘畔庞培佩彭皮平泼普其契恰强乔切钦沁泉让热荣肉儒瑞若萨塞赛桑瑟森莎沙山善绍舍圣施诗石什史士守斯司丝苏素索塔泰坦汤唐陶特提汀图土吐托陀瓦万王旺威韦维魏温文翁沃乌吾武伍西锡希喜夏相香歇谢辛新牙雅亚彦尧叶依伊衣宜义因音英雍尤于约宰泽增詹珍治中仲朱诸卓孜祖佐伽娅尕腓滕济嘉津赖莲琳律略慕妮聂裴浦奇齐琴茹珊卫欣逊札哲智兹芙汶迦珀琪梵斐胥黛"
#define TRANS_RUSSIAN "·阿安奥巴比彼波布察茨大德得丁杜尔法夫伏甫盖格哈基加坚捷金卡科可克库拉莱兰勒雷里历利连列卢鲁罗洛马梅蒙米姆娜涅宁诺帕泼普奇齐乔切日萨色山申什斯索塔坦特托娃维文乌西希谢亚耶叶依伊以扎佐柴达登蒂戈果海赫华霍吉季津柯理琳玛曼穆纳尼契钦丘桑沙舍泰图瓦万雅卓兹"
#define TRANS_JAPANESE "安奥八白百邦保北倍本比滨博步部彩菜仓昌长朝池赤川船淳次村大代岛稻道德地典渡尔繁饭风福冈高工宫古谷关广桂贵好浩和合河黑横恒宏后户荒绘吉纪佳加见健江介金今进井静敬靖久酒菊俊康可克口梨理里礼栗丽利立凉良林玲铃柳隆鹿麻玛美萌弥敏木纳南男内鸟宁朋片平崎齐千前浅桥琴青清庆秋丘曲泉仁忍日荣若三森纱杉山善上伸神圣石实矢世市室水顺司松泰桃藤天田土万望尾未文武五舞西细夏宪相小孝新星行雄秀雅亚岩杨洋阳遥野也叶一伊衣逸义益樱永由有佑宇羽郁渊元垣原远月悦早造则泽增扎宅章昭沼真政枝知之植智治中忠仲竹助椎子佐阪坂堀荻菅薰浜濑鸠筱"
//Translation type
#define TT_ENGLISH 0
#define TT_RUSSIAN 1
#define TT_JAPANESE 2
//Seperator type
#define SEPERATOR_C_SENTENCE "。
!?:;…"
#define SEPERATOR_C_SUB_SENTENCE "、,()“”‘’" #define SEPERATOR_E_SENTENCE "!?:;"
#define SEPERATOR_E_SUB_SENTENCE ",()\042'"
#define SEPERATOR_LINK "\n\r "
//Sentence begin and ending string
#define SENTENCE_BEGIN "始##始"
#define SENTENCE_END "末##末"
//Seperator between two words
#define WORD_SEGMENTER "@"
原子分词后的实例如下图二所示:
查词典,生成词网
经过原子分词后,源字符串成了一个个独立的最小语素单位。
下面的初次切分,就是把原子之间所有可能的组合都先找出来。
算法是用两个循环来实现,第一层遍历整个原子单位,第二层是当找到一个原子时,不断把后面相邻的原子和该原子组合到一起,访问词典库看它能否构成一个有意义有词组。
用数学方法可以做如下描述:
有一个原子序列:A(n)(0<=n<m)(其中m为原子序列A的长度)。
当I=n时,判断
AnAn+1..Ap是否为一个词组,其中n<p<m.
用伪码表示:
for(int I=0;I<m;I++){
String s=A[I];
for(int j=I+1;j<m;j++){
s+=A[j];
if(s是一个词组){
把s加入到初次切分的列表中;
记录该词组的词性;
记录该词组所在表中的坐标位置及其它信息; }
else
break;
}
}
核心词典:CoreNatureDictionary.txt
初次切分后的数据结构如下图一所示:
图一
分词用例”他说的确实在理”经过初次切分后的结果如下图二所示:
图二
用二维表来表示图一中的链表结构如下图二所示:
图三
从上图三可以看出,在二维表中,初次切分后的词组,第一次字相同的在同一行,最后一个字相同的在同一列,原来的原子在对称轴上.
对上述过程进行处理的参考源代码如下:
ICTCLAS解析
bool CSegment::BiSegment(char *sSentence, double dSmoothingPara, CDictionary &dictCore, CDictionary &dictBinary, unsigned int nResultCount)
{
......
//在此处完成上图一的处理结果,生成一个链表结构。