⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 mpwordseg.cpp

📁 最大概率分词法,这种分词算法能够较好的解决汉语分词中的歧义问题,但分词效率比最大匹配分词算法要低
💻 CPP
字号:
#include "stdafx.h"
#include "math.h" // 包含log函数的定义

#include "MPWordSeg.h"
#include "MyDictionary.h"
#include "MyFileApp.h"

/*
# define MaxWordLength 8  // 最大词长为8个字节(即4个汉字)
# define Separator "/  "    // 词界标记

# define CorpusSize 200000 // 定义语料库规模
*/

extern CString Separator;
extern int MaxWordLength;
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=""; // 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();
}

// 最大概率法分词程序结束

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -