希望通过本文能让大家对C#如何快速获取助记码有更深刻的了解,51CTO也向您推荐《C#实用基础教程》。
本文实现:
1、以牺牲空间为代价,方便快速地实现汉字的助记码获取。
2、针对拼音特性,实现多音字并提供显式地姓氏调用方法。
网上关于使用C#取助记码的方法很多,不过一般都是采用将每一个码有哪些汉字的方法来实现助记码的转换,它的缺点是显而易见的:
1.相当于本来应该存放在数据库或者是外部的数据存放到程序中;
2.利用字符串的定位功能,性能上也不一定好。
在线性表中我们查找指定位置结点的数据是最快的,比如一个包含10000个元素的线性表中,无论直接访问哪一个位置的数据值,只要根据表的***个位置就可以直接定位到第9000个元素的位置,数组也是一种简单有效的线性表,需要某个项目,只要通过编移量即可搞定。
在不使用数据库,直接使用文件做资源,方便配置,完成中文到助记码的的转换,同时针对拼音助记码,还要实现:1.多音字词组匹配;2.姓氏专用方法。
同时五笔码、四角码等多种类别也易如反掌。虽然在以牺牲空间为代价,但用空间换得时间的性能提高,还是非常值得的。
数据存放使用数组
数组就是一种线性表,我们的解决思路是:
1、每一个项目对应一个结构,它包括汉字、拼音码、是否多音字、五笔码(或其它更多编码);
2、每一个汉字直接转换成一个数字编码(ASCII或者是UNICODE),数字就是数组下标;
3、读取文字串,根据每一个汉字取得数字,直接定位数组中的位置,取得编码。
因此我们定义用于存放汉字的助记简码的结构(有更多编码,只要在结构上扩充即可):
- private struct ItemWord{
- public int numFlag; //是否多音字
- public char strWord;//当前单词
- public string strSpell1;//输入码
- public string strSpell2;//输入码
- public string strExt; //多音字保留串
- public string strName;//用于定义姓氏码
加载助记码字典
由于要支持拼音及多音字,也支持五笔的助记码,为了例子方便,编码表都暂时存放在d:\myword下,下面以拼音码的加载为例,首先初始化Itemword中的每一个项目,通常情况下GBK的实际汉字数应该在20000字左右,从双字节的角度来看,最多也就只有64K,因此把我们的数组定义为64K,即:
- private static readonly int INTMAX = 65536;
- private ItemWord[] stWord = new ItemWord[INTMAX];
在函数fun_LoadWord中拼音码hzpy1.txt的加载方法:
- while ((strInput = srFile.ReadLine()) != null){
- chrWord = strInput.ToCharArray();
- numIndex = (int)chrWord[0];
- stWord[numIndex].strWord = chrWord[0];
- stWord[numIndex].strSpell1 = chrWord[2].ToString();
注意,其中最最关键是把读取的内容转成char的数组,再通过(int)chrWord[0]把它转换为一个数组的下标值(这个相对来说C就简单许多),再把相应内容填到数组中此下标项目。对于五笔码的读取方法也一致,非常简单(更多的其它编码也不在话下)。不过,拼音码的多音存储,需要介绍一下,实际上对于汉字来说,单音是大多数,只有少部分才是多音字,为了结构更简单,把多音字是存放在相应的汉字结构中,即:
- numIndex = (int)chrWord[0];
- strInput = strInput.Substring(2);
- strInput = strInput.Replace("\t", " ");
- strInput = strInput.Replace(" ", "/");
- stWord[numIndex].strExt = stWord[numIndex].strExt + strInput + "|";
最终strExt中的结构内容是“|曾哥/Z|”的格式,它的好处会在多音码的展现时进行说明。
从汉字到简码
我们先举例五笔的助记码实现,对传入的汉字串来说,只要提取每一个汉字,得到对应的字符编码,再直接提取其对应编码,由于直接采用下标取得编码,而数据又加载在内存数组中,可以用飞速来描述它:
- chrWord = strChinese.ToCharArray();
- numCount = strChinese.Length;
- for (i = 0; i < numCount; i++){
- numIndex = (int)chrWord[i];
- strSpell = strSpell + stWord[numIndex].strSpell2.ToString();
拼音码,则存在着几个主要问题,一个是姓氏,还有一个是多音字,多音字的实现,通过词组模式来处理,即首先它是一个多音字,即结构中的numFlag标志。同时根据汉字串往前往后追踪一个形成词组,例如“曾哥”,它虽然来源于姓氏,但是本身已经形成一个受众很广的词,在hzpy2.txt中定义“曾 曾哥 Z”这样一行内容。在多音字时,我们用函数funFindMulti来定位多音字的编码:
- strWord = "|" + ch1 + ch2 + "/";
- numPos = stWord[numIndex].strExt.IndexOf(strWord);
- if (numPos >= 0) return stWord[numIndex].strExt.Substring(numPos + strWord.Length, 1);
也就是说,但我们定位汉字“曾”时,首先它是一个多音字,再根据词组“曾哥”对应的编码,
我们把用于定位的词组形成前缀“|词组/”的格式,假定有多个多音时,可以通过唯一位置定位后,再取得此串后面的字符,即“Z”,所以当我们输入“信曾哥得自信”时,得到拼音助记码自然就是“XZGDZX”了。因为姓氏的特殊性,并且实际业务中,比较明确地知道,当前的内容是否是姓名,即我们显式调用,而姓名中,也只有在首字时才发生按姓的读音。只要调用参数提供是否姓名的开关(下面的变量IsName,为了文章方便,紧凑格式了)即可:
- if (stWord[numIndex].numFlag > 0 && !IsName){if (i > 0) strThis = funFindMulti(numIndex, chrWord[i - 1], chrWord[i]);
- if (strThis.Length == 0 && i < numCount -1 ) strThis = funFindMulti(numIndex, chrWord[i], chrWord[i + 1]);
- }
- if (IsName && i == 0 && stWord[numIndex].strName.Length > 0)strThis = stWord[numIndex].strName;
- if (strThis.Length == 0)strThis = stWord[numIndex].strSpell1.ToString();
- strSpell = strSpell + strThis;
对于姓名之后是否按词组处理,比如张长春这样的人,如果指定为姓名,可能叫“ZZC”也可能叫“ZCC”,这个很难说。所以在某种程度上,我们解决多音字,只能说是处理“多音字”中的80%问题,还是可以接受的。而且在实际业务中,像“我们这些人参他一本”这样的字串是不会在数据字典中出现的。***看看多单字中有典型意义的“单”字在三种模式下的效果吧:
这样我们不仅可以快速处理多音字,也同时处理了多个助记码的实现。在非WEB应用的业务中,在内存中进行快速助记码的过滤就会非常实用。实例中除了汉字编码外,所提供的五笔编码的汉字量还是不少的。
原文标题:C#快速获取助记码
链接:http://www.cnblogs.com/hzspa/archive/2010/05/11/1732539.html
【编辑推荐】