NameRecognizer.cpp
资源名称:Pwswnr.rar [点击查看]
上传用户:qzyuheng
上传日期:2013-04-28
资源大小:71k
文件大小:7k
源码类别:
词法分析
开发平台:
Visual C++
- #include "stdafx.h"
- #include "math.h" // 包含log函数的定义
- #include "NameRecognizer.h"
- #include "MyDictionary.h"
- # define HZ_NUM 6768
- # define HZ_ID(c1,c2) ((c1)-176) *94 +((c2)-161)
- /*
- # define MaxWordLength 8 // 最大词长为8个字节(即4个汉字)
- # define Separator "/ " // 词界标记
- # define Max2Fee -2.14
- # define Max3Fee -0.80
- # define CorpusSize 200000 // 语料库规模,用于计算词的出现概率
- const int SurNameSize=174000; // 姓名语料库中姓氏用字总数
- const int GivenNameSize=320000; // 姓名语料库中人名用字总数
- */
- extern int MaxWordLength;
- extern CString Separator;
- extern long CorpusSize;
- extern float Max2Fee;
- extern float Max3Fee;
- extern long SurNameSize;
- extern long GivenNameSize;
- # if !pDict
- extern CMyDictionary pDict;
- # endif
- struct NameZi{// 定义一个数组存放每个国标汉字用作姓,和用作人名的次数
- int sName,gName;
- } namezis[HZ_NUM];
- double sFee(CString z)
- { // 根据一个汉字作为姓氏使用的次数计算该汉字作为姓氏的费用
- int wFreq=pDict.GetFreq(z);
- if(wFreq==-1)
- wFreq=0;
- double wFee=-log((double)(wFreq+1)/CorpusSize);
- int id=HZ_ID((unsigned char) z[0], (unsigned char) z[1]);
- namezis[id].sName=pDict.GetSnameFreq(z); // 从数据库中读出一个汉字作为姓氏使用的次数
- if(id>=0 && id<HZ_NUM && namezis[id].sName>0)
- return -log((double)(namezis[id].sName+1)/SurNameSize)-wFee;
- else
- return 20.0;
- }
- double gFee(CString z)
- { // 根据一个汉字作为人名使用的次数计算该汉字作为人名的费用
- int wFreq=pDict.GetFreq(z);
- if(wFreq==-1)
- wFreq=0;
- double wFee=-log((double)(wFreq+1)/CorpusSize); // 汉字作为单字词使用的费用
- int id=HZ_ID((unsigned char)z[0], (unsigned char)z[1]);
- namezis[id].gName=pDict.GetGnameFreq(z); //从数据库中读出一个汉字作为人名使用的次数
- if(id>=0 && id<HZ_NUM) {
- if(namezis[id].gName>0)
- return -log((double)(namezis[id].gName+1)/GivenNameSize)-wFee;// 汉字作为人名使用的费用减去它作为单字词使用的费用
- else {// 如果当前汉字不在姓名用字表hanziname中
- return -log(1/(double)GivenNameSize)-wFee; // 汉字作为人名使用的费用减去它作为单字词使用的费用
- } // C++中如果两个整数相除,默认返回值也是整数,因此如果不在GivenNameSize前加上强制数据类型转换,返回值为0,0的对数无意义
- }
- else
- return 20.0;
- }
- double sgFee(CString sg)
- {// 计算候选姓名的费用
- double fee=pDict.GetFee(sg,TRUE); // 把候选词串作为一个姓名看待:姓+名,查询它在姓名表中的费用值
- if(fee<20.0)
- return fee; // 如果这个名字已经在姓名表中存在,就直接返回它的费用值
- if(sg.GetLength()==4) { // 如果候选词串是两个汉字,在姓名表中不是一个完整的名字,就将它作为“名”来查找
- fee=pDict.GetFee(sg,FALSE);
- if(fee<20.0)
- return fee; // 如果姓名表中有这个“双名”,返回其费用值
- }
- // 如果不是上述情况,就临时调用费用函数计算这个字串作为“姓+名”模式
- // 或者“双名”使用的费用
- CString z=sg.Left(2);
- // int id=HZ_ID((unsigned char)z[0],(unsigned char)z[1]); // 取最左边汉字的GB码
- // namezis[id].sName=pDict.GetSnameFreq(z);
- if(pDict.GetSnameFreq(z)>0) {// 看看这个串最左边的汉字是否是“姓氏”用字
- fee=sFee(sg.Left(2))+gFee(sg.Mid(2,2));
- if(sg.GetLength()==4)
- fee+=-log(0.37);
- else
- fee+=gFee(sg.Right(2))+(-log(0.63));
- }// 实际上,即便最左边的汉字是姓氏用字,它也可能是作为人名使用,比如“辛楣”中的辛,在“赵辛楣”中就是作为人名使用
- else { // 如果不是姓氏用字
- if (sg.GetLength()>=6)
- fee=20;
- else
- if (sg.GetLength()==4) // 如果是两个汉字,就假定是双名,没有姓的词串,来计算它作为人名的费用值
- fee=gFee(sg.Left(2))+gFee(sg.Right(2));
- }
- //////////////////////////////////////////////
- // 以下是陈书中计算汉字串姓名费用值的程序段
- // 假定输入汉字串一定是“姓”+“名”模式
- // fee=sFee(sg.Left(2))+gFee(sg.Mid(2,2));
- // if(sg.GetLength()==4)
- // fee+=-log(0.37);
- // else
- // fee+=gFee(sg.Right(2))+(-log(0.63));
- //////////////////////////////////////////////
- return fee; // 返回计算所得费用值
- }
- BOOL isHomoPair (CMaybeName *p1, CMaybeName *p2)
- {// 判断两个候选姓名是否有相同的起点位置
- if(p1->offset==p2->offset)
- return TRUE;
- else
- return FALSE;
- }
- BOOL isCrossPair (CMaybeName *p1, CMaybeName *p2)
- {// 判断两个候选姓名是否有部分重叠现象
- if(p1->offset==p2->offset || p1->offset+p1->length<=p2->offset || p2->offset+p2->length<=p1->offset)
- return FALSE;
- else
- return TRUE;
- }
- CString CheckStr(CString s1)
- {// 对连续单字形成的串进行检查,看其中是否含有姓名词
- CObArray maybeNames; // 存放候选姓名的动态数组
- CMaybeName *p,*p1,*p2; // 用来访问候选姓名数组元素的指针
- int i,j,len;
- for(i=0;i<s1.GetLength();i+=2) {
- for(len=4;len<=6 && i+len<=s1.GetLength();len+=2) {
- double fee=sgFee(s1.Mid(i,len)); // 计算候选姓名的费用
- if(len==4 && fee>=Max2Fee || len>=6 && fee>=Max3Fee) // 如果不是姓名
- continue; // 继续查下一个候选姓名
- p=new CMaybeName(i,len,fee);
- maybeNames.Add(p); // 如果符合标准,加入到候选姓名组
- }
- }
- BOOL iDeleted = FALSE;
- for(i=0;i<maybeNames.GetSize();) {
- for(j=i+1;j<=i+2 && j<maybeNames.GetSize();) {
- p1=(CMaybeName *)maybeNames[i];
- p2=(CMaybeName *)maybeNames[j];
- if(isHomoPair(p1,p2)) { // 检查两个候选姓名是否有相同起点
- if(p1->fee>p2->fee) {
- maybeNames.RemoveAt(i);
- iDeleted=TRUE;
- break;
- }
- else
- maybeNames.RemoveAt(j);
- }
- else
- if(isCrossPair(p1,p2)) { // 检查两个候选姓名的尾跟头是否有重叠部分
- if(p1->fee<=p2->fee)
- maybeNames.RemoveAt(j);
- else {
- maybeNames.RemoveAt(i);
- iDeleted=TRUE;
- break;
- }
- }
- else
- j++;
- }
- if(!iDeleted)
- i++;
- else
- iDeleted=FALSE;
- }
- // create the target string
- CString s2="";
- if(maybeNames.GetSize()==0) { // 如果不存在候选姓名,将每个单字作为词输出
- for(i=0;i<s1.GetLength();i+=2)
- s2+=s1.Mid(i,2)+Separator;
- return s2;
- }
- for(i=0;i<maybeNames.GetSize(); i++) {
- if(i==0)
- j=0;
- else
- j=p->offset+p->length;
- p=(CMaybeName *)maybeNames[i];
- for(;j<p->offset;j+=2)
- s2+=s1.Mid(j,2)+Separator;
- // plus the single Character word before a name word
- CString w=s1.Mid(p->offset,p->length);
- // s2+=w+Separator;
- s2 = s2 + w + "/nr "; // 直接在识别得到的姓名词后标上词性nr
- // 以下两行是陈书程序段,假定候选姓名是“姓+名”模式,
- // 实际上,也可能仅仅是“名”,不包含“姓”
- // CString sName=w.Left(2), gName=w.Mid(2);
- // pDict.Insert(sName,gName,p->fee);
- // 修改为:
- CString ML_HZ=w.Left(2),gName=w.Mid(2);
- // int id=HZ_ID((unsigned char)ML_HZ[0],(unsigned char)ML_HZ[1]); // 取最左边汉字的GB码
- if(pDict.GetSnameFreq(ML_HZ)>0) // 如果左边这个汉字是姓氏用字
- pDict.Insert(ML_HZ,gName,p->fee); // 就把整个字串作为“姓+名”模式加入到词典
- else // 否则,将单字或者双字作为“名”加入到词典
- if (w.GetLength()<=4)
- pDict.Insert(" ",w,p->fee);
- } // 显然,目前程序无法判断“辛楣”是一个单姓单名,
- // 还是一个缺姓的双名(比如“赵辛楣”)
- for(j=p->offset+p->length;j<s1.GetLength();j+=2)
- s2+=s1.Mid(j,2)+Separator;
- return s2;
- }