【vb精品源码栏目提醒】:网学会员鉴于大家对vb精品源码十分关注,论文会员在此为大家搜集整理了“ICTCLAS分词系统研究 - 软件工程”一文,供大家参考学习
ICTCLAS 分词系统研究(一) 收藏 ICTClAS 分词系统是由中科院计算所的张华平、刘群所开发的一套获得广泛好评的分词系统,难能可贵的是该版的 Free 版开放了源代码,为我们很多初学者提供了宝贵的学习材料。
但有一点不完美的是,该源代码没有配套的文档,阅读起来可能有一定的障碍,尤其是对 C/C不熟的人来说.本人就一直用 Java/
VB 作为主要的开发语言C/C上大学时倒是学过不过工作之后一直没有再使用过语法什么的忘的几乎一干二净了.但语言这东西基本的东西都相通的况且 Java 也是在 C/C的基础上形成的有一定的相似处.阅读一遍源代码主要的语法都应该不成问题了. 虽然在 ICTCLAS 的系统中没有完整的文档说明但是我们可以通过查阅张华平和刘群发表的一些相关论文资料还是可以窥探出主要的思路. 该分词系统的主要是思想是先通过CHMM层叠形马尔可夫模型进行分词通过分层既增加了分词的准确性又保证了分词的效率.共分五层如下图一所示:基本思路:先进行原子切分然后在此基础上进行 N-最短路径粗切分找出前 N 个最符合的切分结果生成二元分词表然后生成分词结果接着进行词性标注并完成主要分词步骤.下面是对源代码的主要内容的研究:1.首先,ICTCLAS 分词程序首先调用CICTCLAS_WinDlg::OnBtnRun开始程序的执行.并且可以从看出它的处理方法是把源字符串分段处理。
并且在分词前,完成词典的加载过程,即生成m_ICTCLAS 对象时调用构造函数完成词典库的加载。
关于词典结构的分析,请参加分词系统研究(二)。
void CICTCLAS_WinDlg::OnBtnRun ......//在此处进行分词和词性标记 ifm_ICTCLAS.ParagraphProcessingchar LPCTSTRm_sSourcesResult m_sResult.Formatquot错误:程序初始化异常!quot else m_sResult.FormatquotsquotsResult//输出最终分词结果 ......2.在 OnBtnRun方法里面调用分段分词处理方法 bool CResult::ParagraphProcessingcharsParagraphchar sResult完成分词的整个处理过程,包括分词的词性标注.其中第一个参数为源字符串,第二个参数为分词后的字符串.在这两个方法中即完成了整个分词处理过程,下面需要了解的是在此方法中,如何调用其它方法一步步按照上图所示的分析框架完成分词过程.为了简单起见,我们先不做未登录词的分析。
//Paragraph Segment and POS Taggingbool CResult::ParagraphProcessingchar sParagraphchar sResult ........ ProcessingsSentence1 //Processing and output the result of current sentence. Outputm_pResult0sSentenceResultbFirstIgnore //Output to the imediate result .......3.主要的分词处理是在 Processing方法里面发生的,下面我们对它进行进一步的分析.bool CResult::Processingchar sSentenceunsigned int nCount......//进行二叉分词m_Seg.BiSegmentsSentencem_dSmoothingParam_dictCorem_dictBigramnCount......//在此处进行词性标注m_POSTagger.POSTaggingm_Seg.m_pWordSegnIndexm_dictCorem_dictCore......4.现在我们先不管词性标注,把注意力集中在二叉分词上,因为这个是分词的两大关键步骤的第一步.参考文章:1.ltlt基于层叠隐马模型的汉语词法分析gtgt刘群 张华平等2.ltlt基于 N-最短路径的中文词语粗分模型gtgt张华平 刘群ICTCLAS 分词系统研究(二)-- -- --词典结构 收藏ICTCLAS 的词典结构是理解分词的重要依据,通过这么一个数据结构设计合理访问速度高效的词典才能达到快速准备的分词的目的。
通过阅读和分析源代码,我们可以知道,是程序运行初,先把词典加载到内存中,以提高访问的速度。
源代码在 Result.cpp 的构造函数 CResult()内实现了词典和分词规则库的加载。
如下代码所示:CResult::CResult……m_dictCore.LoadquotdatacoreDict.dctquotm_POSTagger.LoadContextquotdatalexical.ctxquot……我们再跳进 Load 方法具体分析它是怎样读取数据词典的看 Load 的源代码:bool CDictionary::Loadchar sFilenamebool bReset FILE fp int ijnBuffer3 //首先判断词典文件能否以二进制读取的方式打开 iffpfopensFilenamequotrbquotNULL return false//fail while opening the file //为新文件释放内存空间 for i0iltCC_NUMi//delete the memory of word item array in the dictionary for j0jltm_IndexTablei.nCountj delete m_IndexTablei.pWordItemHeadj.sWord delete m_IndexTablei.pWordItemHead DelModified//删除掉修改过的可以先不管它 //CC_NUM:6768应该是 GB2312 编码中常用汉字的数目 6763 个加上 5 个空位码 fori0iltCC_NUMi //读取一个整形数字词块的数目 freadampm_IndexTablei.nCountsizeofint1fp ifm_IndexTablei.nCountgt0 m_IndexTablei.pWordItemHeadnewWORD_ITEMm_IndexTablei.nCount else m_IndexTablei.pWordItemHead0 continue j0 //根据前面读到的词块数目循环读取一个个词块 whilejltm_IndexTablei.nCount //读取三字整数分别为频度Frequency/词内容长度WordLen/句柄Handle freadnBuffersizeofint3fp m_IndexTablei.pWordItemHeadj.sWordnew charnBuffer11 //读取词内容 ifnBuffer1//String length is more than 0 freadm_IndexTablei.pWordItemHeadj.sWordsizeofcharnBuffer1fp m_IndexTablei.pWordItemHeadj.sWordnBuffer10 ifbReset//Reset the frequency m_IndexTablei.pWordItemHeadj.nFrequency0 else m_IndexTablei.pWordItemHeadj.nFrequencynBuffer0 m_IndexTablei.pWordItemHeadj.nWordLennBuffer1 m_IndexTablei.pWordItemHeadj.nHandlenBuffer2 j1//Get next item in the original table. fclosefp return true看完上面的源代码词典的结构也应该基本清楚了如下图一所示: 图一修改表的数据结构和上图差不多但是在词块数目后面多了一个 nDelete 数目即删除的数目数据结构如下图二所示: 图二GB23121980 年一共收录了 7445 个字符,包括 6763 个汉字和 682 个其它符号。
汉字区的内码范围高字节从 B0-F7,低字节从 A1-FE,占用的码位是 72946768。
其中有 5 个空位是 D7FA-D7FE。
词典库图一所示的 6768 个块即对应 GB2312 编码中的这个 6768 个区位.图一中每一个大块代表以该字开头的所有词组括号内的字为区位码对应的汉字词典表中并不存在为了说明方便才添加上去的.如下所示:块 6759 count:5 wordLen:2 frequency:0 handle:24832 word:黯淡 wordLen:2 frequency:1 handle:24942 word:黯淡 wordLen:2 frequency:3 handle:31232 word:黯然 wordLen:6 frequency:0 handle:27648 word:黯然神伤 wordLen:6 frequency:0 handle:26880 word:黯然失色块 6760 count:1 wordLen:2 frequency:0 handle:28160 word:鼢鼠块 6761 count:2 wordLen:4 frequency:0 handle:28160 word:鼬鼠皮 wordLen:2 frequency:0 handle:28160 word:鼬獾对修改后如何保存的源代码进行分析:bool CDictionary::Savechar sFilename FILE fp int ijnCountnBuffer3 PWORD_CHAIN pCur iffpfopensFilenamequotwbquotNULL return false//fail while opening the file //对图一中所示的 6768 个数据块进行遍历 fori0iltCC_NUMi pCurNULL ifm_pModifyTable //计算修改后有效词块的数目 nCountm_IndexTablei.nCountm_pModifyTablei.nCount-m_pModifyTablei.nDelete fwriteampnCountsizeofint1fp pCurm_pModifyTablei.pWordItemHead j0 //对原表中的词块和修改表中的词块进行遍历并把修改后的添加到原表中 whilepCurNULLampampjltm_IndexTablei.nCount //如果修改表中的词长度小于原表中对应位置的词的长度或者长度相等但 nHandle 值比原表中的小则把修改表中的写入到词典文件当中. ifstrcmppCur-gtdata.sWordm_IndexTablei.pWordItemHeadj.sWordlt0strcmppCur-gtdata.sWordm_IndexTablei.pWordItemHeadj.sWord0ampamppCur-gtdata.nHandleltm_IndexTablei.pWordItemHeadj.nHandle //Output the modified data to the file nBuffer0pCur-gtdata.nFrequency nBuffer1pCur-gtdata.nWordLen nBuffer2pCur-gtdata.nHandle fwritenBuffersizeofint3fp ifnBuffer1//String length is more than 0 fwritepCur-gtdata.sWordsizeofcharnBuffer1fp pCurpCur-gtnext//Get next item in the modify table. //频度 nFrequecy 等于-1 说明该词已被删除跳过它 else ifm_IndexTablei.pWordItemHeadj.nFrequency-1 j1 //如果修改表中的词长度比原表中的长度大或 长度相等但句柄值要多就把原表的词写入的词典文件中 elseifstrcmppCur-gtdata.sWordm_IndexTablei.pWordItemHeadj.sWordgt0strcmppCur-gtdata.sWordm_IndexTablei.pWordItemHeadj.sWord0ampamppCur-gtdata.nHandlegtm_IndexTablei.pWordItemHeadj.nHandle //Output the index table data to the file nBuffer0m_IndexTablei.pWordItemHeadj.nFrequency nBuffer1m_IndexTablei.pWordItemHeadj.nWordLen nBuffer2m_IndexTablei.pWordItemHeadj.nHandle fwritenBuffersizeofint3fp ifnBuffer1//String length is more than 0 fwritem_IndexTablei.pWordItemHeadj.sWordsizeofcharnBuffer1fp j1//Get next item in the original table. //把原表中剩余的词写入的词典文件当中 ifjltm_IndexTablei.nCount whilejltm_IndexTablei.nCount ifm_IndexTablei.pWordItemHeadj.nFrequency-1 //Has been deleted nBuffer0m_IndexTablei.pWordItemHeadj.nFrequency nBuffer1m_IndexTablei.pWordItemHeadj.nWordLen nBuffer2m_IndexTablei.pWordItemHeadj.nHandle fwritenBuffersizeofint3fp ifnBuffer1//String length is more than 0 fwritem_IndexTablei.pWordItemHeadj.sWordsizeofcharnBuffer1fp j1//Get next item in the original table. else////原表已到尾部但修改表还没有遍历完把修改表中剩余的词写入到词典文件当中 whilepCurNULL//Add the rest data to the file. nBuffer0pCur-gtdata.nFrequency nBuffer1pCur-gtdata.nWordLen nBuffer2pCur-gtdata.nHandle fwritenBuffersizeofint3fp ifnBuffer1//String length is more than 0 fwritepCur-gtdata.sWordsizeofcharnBuffer1fp pCurpCur-gtnext//Get next item in the modify table. //不是修改标记则把原表的数据全部写入到词典文件当中 else fwriteampm_IndexTablei.nCountsizeofint1fp //write to the file j0 whilejltm_IndexTablei.nCount nBuffer0m_IndexTablei.pWordItemHeadj.nFrequency nBuffer1m_IndexTablei.pWordItemHeadj.nWordLen nBuffer2m_IndexTablei.pWordItemHeadj.nHandle fwritenBuffersizeofint3fp ifnBuffer1//String length is more than 0 fwritem_IndexTablei.pWordItemHeadj.sWordsizeofcharnBuffer1fp j1//Get next item in the original table. fclosefp return true增加一个词条目:bool CDictionary::AddItemchar sWord int nHandleint nFrequency char sWordAddWORD_MAXLENGTH-2 int nPosnFoundPos PWORD_CHAIN pRetpTemppNext int i0 //预处理去掉词的前后的空格 ifPreProcessingsWord ampnPossWordAddtrue return false //查找词典原表中该词是否存在 ifFindInOriginalTablenPossWordAddnHandleampnFoundPos //The word exists in the original table so add the frequency //Operation in the index table and its items ifm_IndexTablenPos.pWordItemHeadnFoundPos.nFrequency-1 //The word item has been removed m_IndexTablenPos.pWordItemHeadnFoundPos.nFrequencynFrequency ifm_pModifyTable//Not prepare the buffer m_pModifyTablenew MODIFY_TABLECC_NUM memsetm_pModifyTable0CC_NUMsizeofMODIFY_TABLE m_pModifyTablenPos.nDelete-1 else m_IndexTablenPos.pWordItemHeadnFoundPos.nFrequencynFrequency return true //如果修改表为空为它初始化空间 ifm_pModifyTable//Not prepare the buffer m_pModifyTablenew MODIFY_TABLECC_NUM memsetm_pModifyTable0CC_NUMsizeofMODIFY_TABLE //在修改表中查询该词是否存在如果存在增加该词的频度 ifFindInModifyTablenPossWordAddnHandleamppRet ifpRetNULL pRetpRet-gtnext else pRetm_pModifyTablenPos.pWordItemHead pRet-gtdata.nFrequencynFrequency return true //如果没有在修改表中找到则添加进去 pTempnew WORD_CHAIN//Allocate the word chain node ifpTempNULL//Allocate memory failure return false memsetpTemp0sizeofWORD_CHAIN//init it with 0 pTemp-gtdata.nHandlenHandle//store the handle pTemp-gtdata.nWordLenstrlensWordAdd pTemp-gtdata.sWordnew char1pTemp-gtdata.nWordLen strcpypTemp-gtdata.sWordsWordAdd pTemp-gtdata.nFrequencynFrequency pTemp-gtnextNULL //插入到修改表中 ifpRetNULL pNextpRet-gtnext//Get the next item before the current item pRet-gtnextpTemp//link the node to the chain else pNextm_pModifyTablenPos.pWordItemHead m_pModifyTablenPos.pWordItemHeadpTemp//Set the pAdd as the head node pTemp-gtnextpNext//Very important or else it will lose some node //把词块数目加一 m_pModifyTablenPos.nCount//the number increase by one return true删除修改过的词条bool CDictionary::DelModified PWORD_CHAIN pTemppCur ifm_pModifyTable return true forint i0iltCC_NUMi pCurm_pModifyTablei.pWordItemHead //删除链表上的节点 whilepCurNULL pTemppCur pCurpCur-gtnext delete pTemp-gtdata.sWord delete pTemp delete m_pModifyTable m_pModifyTableNULL return true//采用二分法进行查找bool CDictionary::FindInOriginalTableint nInnerCodechar sWordint nHandleintnPosRet PWORD_ITEM pItemsm_IndexTablenInnerCode.pWordItemHeadintnStart0nEndm_IndexTablenInnerCode.nCount-1nMidnStartnEnd/2nCount0nCmpValuewhilenStartltnEnd//Binary search nCmpValuestrcmppItemsnMid.sWordsWord //如果中间那个正好是要查找的 ifnCmpValue0ampamppItemsnMid.nHandlenHandlenHandle-1 ifnPosRet ifnHandle-1//Not very strict match //Add in 2002-1-28 nMid-1 //Get the first item which match the current word whilenMidgt0ampampstrcmppItemsnMid.sWordsWord0 nMid-- ifnMidlt0strcmppItemsnMid.sWordsWord0 nMid nPosRetnMid return true ifnPosRet nPosRetnMid return true//find it elseifnCmpValuelt0nCmpValue0ampamppItemsnMid.nHandleltnHandleampampnHandle-1 nStartnMid1 elseifnCmpValuegt0nCmpValue0ampamppItemsnMid.nHandlegtnHandleampampnHandle-1 nEndnMid-1 nMidnStartnEnd/2 ifnPosRet //Get the previous position nPosRetnMid-1return false//在修改表中查询bool CDictionary::FindInModifyTableint nInnerCodechar sWordintnHandlePWORD_CHAIN pFindRet PWORD_CHAIN pCurpPre ifm_pModifyTableNULL//empty return false pCurm_pModifyTablenInnerCode.pWordItemHead pPreNULL //sWord 相等且句柄nHandle相等 whilepCurNULLampamp_stricmppCur-gtdata.sWordsWordlt0_stricmppCur-gtdata.sWordsWord0ampamppCur-gtdata.nHandleltnHandle //sort the link chain as alphabet pPrepCur pCurpCur-gtnext ifpFindRet pFindRetpPre ifpCurNULL ampamp_stricmppCur-gtdata.sWordsWord0ampamppCur-gtdata.nHandlenHandlenHandlelt0 //The node exists delete the node and return return true return false得到词的类型共三种汉字、分隔符和其他int CDictionary::GetWordTypechar sWord int nTypecharTypeunsigned char sWordnLenstrlensWord ifnLengt0ampampnTypeCT_CHINESEampampIsAllChineseunsigned char sWord return WT_CHINESE//Chinese word else ifnLengt0ampampnTypeCT_DELIMITER return WT_DELIMITER//Delimiter else return WT_OTHER//other invalidICTCLAS 分词系统研究(三)-- -- --原子切分 收藏ICTCLAS 分词的第一步就是原子分词。
但在进行原子切分之前,首先要进行断句处理。
所谓断句,就是根据分隔符、回车换行符等语句的分隔标志,把源字符串.