MPWordSeg.cpp
上传用户:qzyuheng
上传日期:2013-04-28
资源大小:71k
文件大小:8k
源码类别:

词法分析

开发平台:

Visual C++

  1. #include "stdafx.h"
  2. #include "math.h" // 包含log函数的定义
  3. #include "MPWordSeg.h"
  4. #include "MyDictionary.h"
  5. #include "MyFileApp.h"
  6. #include "NameRecognizer.h"
  7. /*
  8. # define MaxWordLength 8  // 最大词长为8个字节(即4个汉字)
  9. # define Separator "/  "    // 词界标记
  10. # define CorpusSize 200000
  11. */
  12. extern int MaxWordLength;
  13. extern CString Separator;
  14. extern long CorpusSize;
  15. CMyDictionary pDict; // 定义一个词典类对象,全局变量
  16. // 以下是最大概率法分词程序
  17. struct Candidate {
  18. short offset, length;  // 候选词在输入串中的起点,长度
  19. short goodPrev;  // 最佳前趋词的序号
  20. float fee, sumFee; // 候选词的费用,路径上的累计费用
  21. } Candidates[100];// 假定最多100个候选词
  22. short getTmpWords(CString &s)
  23. { // 从输入串中挑选可能是词的单位作为最大概率法分词的候选词
  24. short i=0,j,len,restlen,n=s.GetLength();
  25. long freq;
  26. CString w;
  27. for(j=0;j<n;j+=2) {
  28. for(len=2;len<=MaxWordLength;len+=2) {
  29. restlen=n-j; 
  30. if (len<=restlen) // 如果剩余词长度不够长,跳出循环
  31. w=s.Mid(j,len);
  32. else
  33. break;
  34. freq=pDict.GetFreq(w); // 如果在数据库中将wfreq字段设为双精度型数字,则返回0值
  35. if(len>2 && freq==-1)
  36. continue;
  37. if(freq==-1) 
  38. freq=0;
  39. Candidates[i].offset=j;
  40. Candidates[i].length=len;
  41. Candidates[i].fee = (float)(-log((double)(freq+1)/CorpusSize));
  42. Candidates[i].sumFee=0.0F;// 置初值
  43. i++;
  44. }
  45. }
  46. return i;
  47. }
  48. void getPrev(short i)
  49. { // 计算每一个候选词的最佳前趋词,以及当前词的最小累计费用
  50. if(Candidates[i].offset==0) {
  51. Candidates[i].goodPrev=-1;
  52. Candidates[i].sumFee=Candidates[i].fee;
  53. return;
  54. }
  55. short j,minID=-1;
  56. // for(j=i-1;j>=0;j--) {
  57. // if(Candidates[j].offset+Candidates[j].length==Candidates[i].offset)
  58. // break;
  59. // }
  60. // for(;Candidates[j].offset+Candidates[j].length==Candidates[i].offset;j--)
  61. for(j=i-1;j>=0;j--) 
  62. { //向左查找所有候选词,得到前驱词集合,从中挑选最佳前趋词
  63. if(Candidates[j].offset+Candidates[j].length==Candidates[i].offset) {
  64. if(minID==-1 || Candidates[j].sumFee<=Candidates[minID].sumFee)
  65. minID=j;
  66. }
  67. if(Candidates[i].offset-Candidates[j].offset>=8) // 向左查找候选词最远不超过4个汉字
  68. break;
  69. }
  70. Candidates[i].goodPrev=minID;
  71. Candidates[i].sumFee=Candidates[i].fee+Candidates[minID].sumFee;
  72. return;
  73. }
  74. CString SegmentHzStrMP(CString s1)
  75. {//最大概率法分词程序,处理一个字符串
  76. int len=s1.GetLength();
  77. short n=getTmpWords(s1); // 获得候选词
  78. short minID=-1;
  79. short i;
  80. //////////////////////////////////
  81. // 计算最大概率程序段开始
  82. for(i=0;i<n;i++) {
  83. getPrev(i); // 获得最佳左邻词
  84. if(Candidates[i].offset+Candidates[i].length==len) { // 如果当前词是s1中最后一个可能的候选词
  85. if(minID==-1||Candidates[i].sumFee<Candidates[minID].sumFee) // 如果这个末尾候选词的累计费用最小
  86. minID=i; // 把当前词的序号赋给minID,这就是最小费用路径的终点词的序号
  87.  // 这就是最后分词结果最右边的那个词的序号
  88. }
  89. }
  90. // 计算最大概率程序段结束
  91. //////////////////////////////////
  92. //////////////////////////////////
  93. // 以下是输出分词结果程序段
  94. CString s2=""; 
  95. /* 
  96.     // 以下是最大概率法分词输出分词结果开始
  97. for(i=minID;i>=0;i=Candidates[i].goodPrev) // 从右向左取候选词
  98. s2=s1.Mid(Candidates[i].offset,Candidates[i].length)+Separator+s2; 
  99. // 最大概率法分词输出分词结果结束
  100. */
  101. ///////////////////////////////////////////////////
  102. // 识别中文姓名程序段开始
  103. CString tmp="";
  104. for(i=minID;i>=0;i=Candidates[i].goodPrev) {
  105. CString w=s1.Mid(Candidates[i].offset,Candidates[i].length);
  106. if(w.GetLength()==2) // 如果是单字,就将它加入到tmp串中
  107. tmp=w+tmp;
  108. else {
  109. if(tmp.GetLength()>0) {
  110. if(tmp.GetLength()==2)
  111. s2=tmp+Separator+s2;
  112. else
  113. // s2=CheckStr(tmp)+Separator+s2;
  114. s2=CheckStr(tmp)+s2; // CheckStr has already output a separator after each word
  115. tmp="";
  116. }
  117. s2=w+Separator+s2;
  118. }
  119. }
  120. if(tmp.GetLength()>0) {
  121. if(tmp.GetLength()==2)
  122. s2=tmp+Separator+" "+s2;
  123. else
  124. // s2=CheckStr(tmp)+Separator+s2;
  125. s2=CheckStr(tmp)+s2;
  126. tmp="";
  127. }
  128. // 识别中文姓名程序段结束
  129. /////////////////////////////////////////////////////
  130. return s2;
  131. }
  132. CString SegmentSentenceMP (CString s1)  
  133. {// 最大概率法分词程序:对句子进行分词处理的函数
  134. CString s2="";
  135. int i,dd;
  136. while(!s1.IsEmpty()) {
  137. unsigned char ch=(unsigned char) s1[0];
  138. if(ch<128) { // 处理西文字符
  139. i=1;
  140. dd=s1.GetLength();
  141. while(i<dd && ((unsigned char)s1[i]<128) && (s1[i]!=10) && (s1[i]!=13)) // s1[i]不能是换行符或回车符
  142. i++;
  143. if ((ch!=32) && (ch!=10) && (ch!=13)) // 如果不是西文空格或换行或回车符
  144. s2 += s1.Left(i) + Separator;
  145. else {
  146. if (ch==10 || ch==13)   // 如果是换行或回车符,将它拷贝给s2输出
  147. s2+=s1.Left(i);
  148. }
  149. s1=s1.Mid(i);
  150. continue;
  151. }
  152. else { 
  153. if (ch<176) { // 中文标点等非汉字字符
  154. i=0;
  155. dd=s1.GetLength();
  156. while(i<dd && ((unsigned char)s1[i]<176) && ((unsigned char)s1[i]>=161)
  157. && (!((unsigned char)s1[i]==161 && ((unsigned char)s1[i+1]>=162 && (unsigned char)s1[i+1]<=168)))
  158. && (!((unsigned char)s1[i]==161 && ((unsigned char)s1[i+1]>=171 && (unsigned char)s1[i+1]<=191)))
  159. && (!((unsigned char)s1[i]==163 && ((unsigned char)s1[i+1]==172 || (unsigned char)s1[i+1]==161) 
  160. || (unsigned char)s1[i+1]==168 || (unsigned char)s1[i+1]==169 || (unsigned char)s1[i+1]==186
  161. || (unsigned char)s1[i+1]==187 || (unsigned char)s1[i+1]==191))) // 
  162. i=i+2; // 假定没有半个汉字
  163. if (i==0)
  164. i=i+2;
  165. if (!(ch==161 && (unsigned char)s1[1]==161)) // 不处理中文空格
  166. s2+=s1.Left(i) + Separator; // 其他的非汉字双字节字符可能连续输出
  167. s1=s1.Mid(i);
  168. continue;
  169. }
  170. }
  171. // 以下处理汉字串
  172. i=2;
  173. dd=s1.GetLength();
  174. while(i<dd && (unsigned char)s1[i]>=176) 
  175. // while(i<dd && (unsigned char)s1[i]>=128 && (unsigned char)s1[i]!=161)
  176. i+=2;
  177. s2+=SegmentHzStrMP(s1.Left(i));
  178. s1=s1.Mid(i);
  179. }
  180. /////////////////////////////////////////////////////
  181. // 以下程序段用于将表示时间的单位合并成一个分词单位
  182. int TmpPos;
  183. const char * p;
  184. CString s2_part_1;
  185. if (s2.Find("  年/")>=0) {
  186. TmpPos=s2.Find("  年/");
  187. s2_part_1=s2.Mid(0,TmpPos);
  188. p=(LPCTSTR) s2_part_1;
  189. p=p+TmpPos-2;
  190. 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') {
  191. s2_part_1=s2_part_1.Mid(0,TmpPos-1);
  192. s2=s2_part_1+s2.Mid(TmpPos+2);
  193. }
  194. }
  195. if (s2.Find("  月/")>=0) {
  196. TmpPos=s2.Find("  月/");
  197. s2_part_1=s2.Mid(0,TmpPos);
  198. p=(LPCTSTR) s2_part_1;
  199. p=p+TmpPos-2;
  200. 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') {
  201. s2_part_1=s2_part_1.Mid(0,TmpPos-1);
  202. s2=s2_part_1+s2.Mid(TmpPos+2);
  203. }
  204. }
  205. if (s2.Find("  日/")>=0) {
  206. TmpPos=s2.Find("  日/");
  207. s2_part_1=s2.Mid(0,TmpPos);
  208. p=(LPCTSTR) s2_part_1;
  209. p=p+TmpPos-2;
  210. 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') {
  211. s2_part_1=s2_part_1.Mid(0,TmpPos-1);
  212. s2=s2_part_1+s2.Mid(TmpPos+2);
  213. }
  214. }
  215. ////////    合并时间单位程序段结束
  216. ///////////////////////////////////////////////
  217. return s2;
  218. }
  219. void SegmentAFileMP (CString FileName)
  220. {  // 最大概率法分词程序:对文件进行分词处理
  221. if (pDict.myDatabaseName.IsEmpty()) {
  222. AfxMessageBox("您没有打开词库,无法进行分词处理");
  223. if(pDict.OpenMDB()==FALSE)
  224. return;
  225. }
  226. FILE * in, * out;
  227. in = fopen((const char*) FileName,"rt");
  228. if(in==NULL) {
  229. AfxMessageBox("无法打开文件");
  230. return;
  231. }
  232. FileName=ChangeFileName(FileName,"-seg");
  233. out = fopen((const char*) FileName,"wt");
  234. if(out==NULL) {
  235. AfxMessageBox("无法创建文件");
  236. fclose(in);
  237. return;
  238. }
  239. CStdioFile inFile(in),outFile(out);
  240. char s[2048];
  241. CString line;
  242. while(inFile.ReadString(s,2048)) {// 循环读入文件中的每一行
  243. line = s;
  244. line = SegmentSentenceMP(line); // 调用句子分词函数进行分词处理
  245. outFile.WriteString(line); // 将分词结果写入目标文件
  246. }
  247. inFile.Close();
  248. outFile.Close();
  249. }
  250. // 最大概率法分词程序结束