MPWordSeg.cpp
资源名称:Pwswnr.rar [点击查看]
上传用户:qzyuheng
上传日期:2013-04-28
资源大小:71k
文件大小:8k
源码类别:
词法分析
开发平台:
Visual C++
- #include "stdafx.h"
- #include "math.h" // 包含log函数的定义
- #include "MPWordSeg.h"
- #include "MyDictionary.h"
- #include "MyFileApp.h"
- #include "NameRecognizer.h"
- /*
- # define MaxWordLength 8 // 最大词长为8个字节(即4个汉字)
- # define Separator "/ " // 词界标记
- # define CorpusSize 200000
- */
- extern int MaxWordLength;
- extern CString Separator;
- extern long CorpusSize;
- CMyDictionary pDict; // 定义一个词典类对象,全局变量
- // 以下是最大概率法分词程序
- struct Candidate {
- short offset, length; // 候选词在输入串中的起点,长度
- short goodPrev; // 最佳前趋词的序号
- float fee, sumFee; // 候选词的费用,路径上的累计费用
- } Candidates[100];// 假定最多100个候选词
- short getTmpWords(CString &s)
- { // 从输入串中挑选可能是词的单位作为最大概率法分词的候选词
- short i=0,j,len,restlen,n=s.GetLength();
- long freq;
- CString w;
- for(j=0;j<n;j+=2) {
- for(len=2;len<=MaxWordLength;len+=2) {
- restlen=n-j;
- if (len<=restlen) // 如果剩余词长度不够长,跳出循环
- w=s.Mid(j,len);
- else
- break;
- freq=pDict.GetFreq(w); // 如果在数据库中将wfreq字段设为双精度型数字,则返回0值
- if(len>2 && freq==-1)
- continue;
- if(freq==-1)
- freq=0;
- Candidates[i].offset=j;
- Candidates[i].length=len;
- Candidates[i].fee = (float)(-log((double)(freq+1)/CorpusSize));
- Candidates[i].sumFee=0.0F;// 置初值
- i++;
- }
- }
- return i;
- }
- void getPrev(short i)
- { // 计算每一个候选词的最佳前趋词,以及当前词的最小累计费用
- if(Candidates[i].offset==0) {
- Candidates[i].goodPrev=-1;
- Candidates[i].sumFee=Candidates[i].fee;
- return;
- }
- short j,minID=-1;
- // for(j=i-1;j>=0;j--) {
- // if(Candidates[j].offset+Candidates[j].length==Candidates[i].offset)
- // break;
- // }
- // for(;Candidates[j].offset+Candidates[j].length==Candidates[i].offset;j--)
- for(j=i-1;j>=0;j--)
- { //向左查找所有候选词,得到前驱词集合,从中挑选最佳前趋词
- if(Candidates[j].offset+Candidates[j].length==Candidates[i].offset) {
- if(minID==-1 || Candidates[j].sumFee<=Candidates[minID].sumFee)
- minID=j;
- }
- if(Candidates[i].offset-Candidates[j].offset>=8) // 向左查找候选词最远不超过4个汉字
- break;
- }
- Candidates[i].goodPrev=minID;
- Candidates[i].sumFee=Candidates[i].fee+Candidates[minID].sumFee;
- return;
- }
- CString SegmentHzStrMP(CString s1)
- {//最大概率法分词程序,处理一个字符串
- int len=s1.GetLength();
- short n=getTmpWords(s1); // 获得候选词
- short minID=-1;
- short i;
- //////////////////////////////////
- // 计算最大概率程序段开始
- for(i=0;i<n;i++) {
- getPrev(i); // 获得最佳左邻词
- if(Candidates[i].offset+Candidates[i].length==len) { // 如果当前词是s1中最后一个可能的候选词
- if(minID==-1||Candidates[i].sumFee<Candidates[minID].sumFee) // 如果这个末尾候选词的累计费用最小
- minID=i; // 把当前词的序号赋给minID,这就是最小费用路径的终点词的序号
- // 这就是最后分词结果最右边的那个词的序号
- }
- }
- // 计算最大概率程序段结束
- //////////////////////////////////
- //////////////////////////////////
- // 以下是输出分词结果程序段
- CString s2="";
- /*
- // 以下是最大概率法分词输出分词结果开始
- for(i=minID;i>=0;i=Candidates[i].goodPrev) // 从右向左取候选词
- s2=s1.Mid(Candidates[i].offset,Candidates[i].length)+Separator+s2;
- // 最大概率法分词输出分词结果结束
- */
- ///////////////////////////////////////////////////
- // 识别中文姓名程序段开始
- CString tmp="";
- for(i=minID;i>=0;i=Candidates[i].goodPrev) {
- CString w=s1.Mid(Candidates[i].offset,Candidates[i].length);
- if(w.GetLength()==2) // 如果是单字,就将它加入到tmp串中
- tmp=w+tmp;
- else {
- if(tmp.GetLength()>0) {
- if(tmp.GetLength()==2)
- s2=tmp+Separator+s2;
- else
- // s2=CheckStr(tmp)+Separator+s2;
- s2=CheckStr(tmp)+s2; // CheckStr has already output a separator after each word
- tmp="";
- }
- s2=w+Separator+s2;
- }
- }
- if(tmp.GetLength()>0) {
- if(tmp.GetLength()==2)
- s2=tmp+Separator+" "+s2;
- else
- // s2=CheckStr(tmp)+Separator+s2;
- s2=CheckStr(tmp)+s2;
- tmp="";
- }
- // 识别中文姓名程序段结束
- /////////////////////////////////////////////////////
- return s2;
- }
- CString SegmentSentenceMP (CString s1)
- {// 最大概率法分词程序:对句子进行分词处理的函数
- CString s2="";
- int i,dd;
- while(!s1.IsEmpty()) {
- unsigned char ch=(unsigned char) s1[0];
- if(ch<128) { // 处理西文字符
- i=1;
- dd=s1.GetLength();
- while(i<dd && ((unsigned char)s1[i]<128) && (s1[i]!=10) && (s1[i]!=13)) // s1[i]不能是换行符或回车符
- i++;
- if ((ch!=32) && (ch!=10) && (ch!=13)) // 如果不是西文空格或换行或回车符
- s2 += s1.Left(i) + Separator;
- else {
- if (ch==10 || ch==13) // 如果是换行或回车符,将它拷贝给s2输出
- s2+=s1.Left(i);
- }
- s1=s1.Mid(i);
- continue;
- }
- else {
- if (ch<176) { // 中文标点等非汉字字符
- i=0;
- dd=s1.GetLength();
- while(i<dd && ((unsigned char)s1[i]<176) && ((unsigned char)s1[i]>=161)
- && (!((unsigned char)s1[i]==161 && ((unsigned char)s1[i+1]>=162 && (unsigned char)s1[i+1]<=168)))
- && (!((unsigned char)s1[i]==161 && ((unsigned char)s1[i+1]>=171 && (unsigned char)s1[i+1]<=191)))
- && (!((unsigned char)s1[i]==163 && ((unsigned char)s1[i+1]==172 || (unsigned char)s1[i+1]==161)
- || (unsigned char)s1[i+1]==168 || (unsigned char)s1[i+1]==169 || (unsigned char)s1[i+1]==186
- || (unsigned char)s1[i+1]==187 || (unsigned char)s1[i+1]==191))) //
- i=i+2; // 假定没有半个汉字
- if (i==0)
- i=i+2;
- if (!(ch==161 && (unsigned char)s1[1]==161)) // 不处理中文空格
- s2+=s1.Left(i) + Separator; // 其他的非汉字双字节字符可能连续输出
- s1=s1.Mid(i);
- continue;
- }
- }
- // 以下处理汉字串
- i=2;
- dd=s1.GetLength();
- while(i<dd && (unsigned char)s1[i]>=176)
- // while(i<dd && (unsigned char)s1[i]>=128 && (unsigned char)s1[i]!=161)
- i+=2;
- s2+=SegmentHzStrMP(s1.Left(i));
- s1=s1.Mid(i);
- }
- /////////////////////////////////////////////////////
- // 以下程序段用于将表示时间的单位合并成一个分词单位
- int TmpPos;
- const char * p;
- CString s2_part_1;
- if (s2.Find(" 年/")>=0) {
- TmpPos=s2.Find(" 年/");
- s2_part_1=s2.Mid(0,TmpPos);
- p=(LPCTSTR) s2_part_1;
- p=p+TmpPos-2;
- if (p[0]=='1'||p[0]=='2'||p[0]=='3'||p[0]=='4'||p[0]=='5'||p[0]=='6'||p[0]=='7'||p[0]=='8'||p[0]=='9'||p[0]=='0') {
- s2_part_1=s2_part_1.Mid(0,TmpPos-1);
- s2=s2_part_1+s2.Mid(TmpPos+2);
- }
- }
- if (s2.Find(" 月/")>=0) {
- TmpPos=s2.Find(" 月/");
- s2_part_1=s2.Mid(0,TmpPos);
- p=(LPCTSTR) s2_part_1;
- p=p+TmpPos-2;
- if (p[0]=='1'||p[0]=='2'||p[0]=='3'||p[0]=='4'||p[0]=='5'||p[0]=='6'||p[0]=='7'||p[0]=='8'||p[0]=='9'||p[0]=='0') {
- s2_part_1=s2_part_1.Mid(0,TmpPos-1);
- s2=s2_part_1+s2.Mid(TmpPos+2);
- }
- }
- if (s2.Find(" 日/")>=0) {
- TmpPos=s2.Find(" 日/");
- s2_part_1=s2.Mid(0,TmpPos);
- p=(LPCTSTR) s2_part_1;
- p=p+TmpPos-2;
- if (p[0]=='1'||p[0]=='2'||p[0]=='3'||p[0]=='4'||p[0]=='5'||p[0]=='6'||p[0]=='7'||p[0]=='8'||p[0]=='9'||p[0]=='0') {
- s2_part_1=s2_part_1.Mid(0,TmpPos-1);
- s2=s2_part_1+s2.Mid(TmpPos+2);
- }
- }
- //////// 合并时间单位程序段结束
- ///////////////////////////////////////////////
- return s2;
- }
- void SegmentAFileMP (CString FileName)
- { // 最大概率法分词程序:对文件进行分词处理
- if (pDict.myDatabaseName.IsEmpty()) {
- AfxMessageBox("您没有打开词库,无法进行分词处理");
- if(pDict.OpenMDB()==FALSE)
- return;
- }
- FILE * in, * out;
- in = fopen((const char*) FileName,"rt");
- if(in==NULL) {
- AfxMessageBox("无法打开文件");
- return;
- }
- FileName=ChangeFileName(FileName,"-seg");
- out = fopen((const char*) FileName,"wt");
- if(out==NULL) {
- AfxMessageBox("无法创建文件");
- fclose(in);
- return;
- }
- CStdioFile inFile(in),outFile(out);
- char s[2048];
- CString line;
- while(inFile.ReadString(s,2048)) {// 循环读入文件中的每一行
- line = s;
- line = SegmentSentenceMP(line); // 调用句子分词函数进行分词处理
- outFile.WriteString(line); // 将分词结果写入目标文件
- }
- inFile.Close();
- outFile.Close();
- }
- // 最大概率法分词程序结束