|
extern CString Separator; extern int MaxWordLength; extern long CorpusSize; CMyDictionary pDict; // 定义一个词典类对象,全局变量
// 以下是最大概率法分词程序
struct Candidate { short offset, length; // 候选词在输入串中的起点,长度 short goodPrev; // 最佳前趋词的序号 float fee, sumFee; // 候选词的费用,路径上的累计费用 } Candidates[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;
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(); }
// 最大概率法分词程序结束 |