一、关于GB2312编码的必要知识
GB2312编码适用于汉字处理、汉字通信等系统之间的信息交换,通行于中国大陆;新加坡等地也采用此编码。中国大陆几乎所有的中文系统和国际化的软件都支持GB 2312。1995年又颁布了《汉字编码扩展规范》(GBK)。GBK与GB 2312—1980国家标准所对应的内码标准兼容,同时在字汇一级支持 ISO/IEC10646—1 和 GB 13000—1 的全部中、日、韩(CJK)汉字,共计20902字。
1 分区表示
基本集共收入汉字6763个和非汉字图形字符682个。整个字符集分成94个区,每区有94个位。每个区位上只有一个字符,因此可用所在的区和位来对汉字进行编码,称为区位码。
01-09区为特殊符号。
16-55区为一级汉字,按拼音排序。
56-87区为二级汉字,按部首/笔画排序。
10-15区及88-94区则未有编码。
举例来说,“啊”字是GB2312之中的第一个汉字,它的区位码就是1601。
2 国标码和计算机机内码
把换算成十六进制的区位码加上2020H,就得到国标码。国标码加上8080H,就得到常用的计算机机内码。
说到这里,有同学要问了,区位码不是已经可以表示汉字了吗?为什么还要搞出来了国标码和计算机内码?听我给你慢慢道来。
GB2312虽然说是对中文编码,但是里面有对26个英文字母和一些特殊符号的编码,按理说这和ASCII重合的部分应该无需设置,沿用ASCII中不就行了?但是当时在制定GB2312之前,就决定覆盖掉ASCII中符号和英文字母部分,所以将其中的英文字母和符号重新编入GB2312中。而对于ASCII中前32个控制字符则继续沿用。所以保留前32字符,就需要将汉字编码向后偏移32,十六进制20H,这也就是区位码要加上20H得到国标码,这就是GB2312的编码规范。
好了,解释清楚为什么要从区位码转为国标码,那为什么国标码还要转为机内码呢?这是为了避免和ASCII码编码的字符冲突。还以“啊”为例,它的区位码为1601,那么国标码就是区码和位码分别加上20H,得到3021H,那么计算机在读取这个数字的时候,首先读取30,是个控制位,然后读取21,还是个控制位,这样就会出现乱码。为了解决这个问题,汉字机内码就横空出世了。
先说定义,汉字的机内码是计算机系统内部对汉字进行存储、处理、传输统一使用的代码,又称为汉字内码。大家要明白,计算机在实际存储一个汉字时,实际上存储的不是区位码或者国标码,而是机内码。而机内码实际上是汉字的区位码分别加上A0H。因为ASCII中使用7位,最高位为0;而汉字的机内码在区位码加上A0H后,字节的最高位变为1了,这样就区分开了ASCII和GB2312。
还以“啊”为例,它的区位码是1601,但实际上它在计算机内部存储的的编码(假定执行时采用GBK编码方式)是B0A1H(注意,存储的第一个字节为B0,第二个字节为A1).
3 字节结构
在使用GB2312的程序中,通常采用EUC储存方法,以便兼容于ASCII。每个汉字及符号以两个字节来表示。第一个字节称为“高位字节”(也称“区字节”),第二个字节称为“低位字节”(也称“位字节”)。
“高位字节”使用了0xA1-0xF7(把01-87区的区号加上0xA0),“低位字节”使用了0xA1-0xFE(把01-94加上 0xA0)。 由于一级汉字从16区起始,汉字区的“高位字节”的范围是0xB0-0xF7,“低位字节”的范围是0xA1-0xFE,占用的码位是 72*94=6768。其中有5个空位是D7FA-D7FE。
了解了上述基础知识之后,我们就可以知道了,计算机获取了汉字的ANSI编码之后,高位字节和低位字节分别减去0Xa0之后,就可以得到区位码。再根据汉字的区位码,确定汉字是一级还是二级汉字,确定其拼音首字母。
二、代码实现
//该函数可以获取一个汉字字符串的拼音首字母
//strSrc为输入的源字符串,strResult为返回的拼音首字母字符串
void GetWord1stLetter(const CString& strSrc, CString & strResult)
{
strResult.Empty();
if (strSrc.IsEmpty())
return;
byte ucHigh, ucLow;
int nCode;
CString strRet;
//由于GetString返回的是wchar_t类型的字符串,而宽字符类型在程序执行时采用Unicode编码规范,
//UTF-16编码方式;因此,为了找出和GB2312的对应关系,需要首先转换为ANSI编码方式
std::string strAnsi = CT2A(strSrc.GetString());
size_t nLength = strAnsi.length();
for (size_t i = 0; i < nLength; i++)
{
if ((unsigned)strAnsi[i] < 0x80) //accii码
{
strResult += strAnsi[i];
continue;
}
//获取汉字的区位码。由于汉字的机内码采用EUC-CN编码方式,因此第1个字节就是高位字节,
//第2个字节就是低位字节,这两个字节分别减去0xa0,就是汉字的区位码
if (i + 1 < nLength) //防止越界
{
ucHigh = (byte)strAnsi[i];
ucLow = (byte)strAnsi[i + 1];
if (ucHigh < 0xa1 || ucLow < 0xa1)
continue;
else
nCode = (ucHigh - 0xa0) * 100 + ucLow - 0xa0;
GetChs1stLetter(nCode, strRet);
strResult += strRet;
i++;
}
}
}
//该接口根据汉字区位码获取汉字的拼音首字符
void GetChs1stLetter(int nCode, CString& strLetter)
{
if (nCode <= 1600)
return;
//一级汉字按照拼音顺序排序,该数组存储汉字拼音分隔位置
static int ari1stSecPosValue[] =
{
1601, 1637, 1833, 2078, 2274, 2302, 2433, 2594, 2787, 3106, 3212,
3472, 3635, 3722, 3730, 3858, 4027, 4086, 4390, 4558, 4684, 4925, 5249
};
//存储汉语拼音的首字母
static TCHAR psz1stLetter[] = _T("ABCDEFGHJKLMNOPQRSTWXYZ");
//存储二级汉字拼音的首字母
static TCHAR psz2ndSecTbl[] =
_T("CJWGNSPGCGNE[Y[BTYYZDXYKYGT[JNNJQMBSGZSCYJSYY[PGKBZGY[YWJKGKLJYWKPJQHY[W[DZLSGMRYPYWWCCKZNKYYGTTNJJNYKKZYTCJNMCYLQLYPYQFQRPZSLWBTGKJFYXJWZLTBNCXJJJJTXDTTSQZYCDXXHGCK[PHFFSS[YBGXLPPBYLL[HLXS[ZM[JHSOJNGHDZQYKLGJHSGQZHXQGKEZZWYSCSCJXYEYXADZPMDSSMZJZQJYZC[J[WQJBYZPXGZNZCPWHKXHQKMWFBPBYDTJZZKQHY")
_T("LYGXFPTYJYYZPSZLFCHMQSHGMXXSXJ[[DCSBBQBEFSJYHXWGZKPYLQBGLDLCCTNMAYDDKSSNGYCSGXLYZAYBNPTSDKDYLHGYMYLCXPY[JNDQJWXQXFYYFJLEJPZRXCCQWQQSBNKYMGPLBMJRQCFLNYMYQMSQYRBCJTHZTQFRXQHXMJJCJLXQGJMSHZKBSWYEMYLTXFSYDSWLYCJQXSJNQBSCTYHBFTDCYZDJWYGHQFRXWCKQKXEBPTLPXJZSRMEBWHJLBJSLYYSMDXLCLQKXLHXJRZJMFQHXHWY")
_T("WSBHTRXXGLHQHFNM[YKLDYXZPYLGG[MTCFPAJJZYLJTYANJGBJPLQGDZYQYAXBKYSECJSZNSLYZHSXLZCGHPXZHZNYTDSBCJKDLZAYFMYDLEBBGQYZKXGLDNDNYSKJSHDLYXBCGHXYPKDJMMZNGMMCLGWZSZXZJFZNMLZZTHCSYDBDLLSCDDNLKJYKJSYCJLKWHQASDKNHCSGANHDAASHTCPLCPQYBSDMPJLPZJOQLCDHJJYSPRCHN[NNLHLYYQYHWZPTCZGWWMZFFJQQQQYXACLBHKDJXDGMMY")
_T("DJXZLLSYGXGKJRYWZWYCLZMSSJZLDBYD[FCXYHLXCHYZJQ[[QAGMNYXPFRKSSBJLYXYSYGLNSCMHZWWMNZJJLXXHCHSY[[TTXRYCYXBYHCSMXJSZNPWGPXXTAYBGAJCXLY[DCCWZOCWKCCSBNHCPDYZNFCYYTYCKXKYBSQKKYTQQXFCWCHCYKELZQBSQYJQCCLMTHSYWHMKTLKJLYCXWHEQQHTQH[PQ[QSCFYMNDMGBWHWLGSLLYSDLMLXPTHMJHWLJZYHZJXHTXJLHXRSWLWZJCBXMHZQXSDZP")
_T("MGFCSGLSXYMJSHXPJXWMYQKSMYPLRTHBXFTPMHYXLCHLHLZYLXGSSSSTCLSLDCLRPBHZHXYYFHB[GDMYCNQQWLQHJJ[YWJZYEJJDHPBLQXTQKWHLCHQXAGTLXLJXMSL[HTZKZJECXJCJNMFBY[SFYWYBJZGNYSDZSQYRSLJPCLPWXSDWEJBJCBCNAYTWGMPAPCLYQPCLZXSBNMSGGFNZJJBZSFZYNDXHPLQKZCZWALSBCCJX[YZGWKYPSGXFZFCDKHJGXDLQFSGDSLQWZKXTMHSBGZMJZRGLYJB")
_T("PMLMSXLZJQQHZYJCZYDJWBMYKLDDPMJEGXYHYLXHLQYQHKYCWCJMYYXNATJHYCCXZPCQLBZWWYTWBQCMLPMYRJCCCXFPZNZZLJPLXXYZTZLGDLDCKLYRZZGQTGJHHGJLJAXFGFJZSLCFDQZLCLGJDJCSNZLLJPJQDCCLCJXMYZFTSXGCGSBRZXJQQCTZHGYQTJQQLZXJYLYLBCYAMCSTYLPDJBYREGKLZYZHLYSZQLZNWCZCLLWJQJJJKDGJZOLBBZPPGLGHTGZXYGHZMYCNQSYCYHBHGXKAMTX")
_T("YXNBSKYZZGJZLQJDFCJXDYGJQJJPMGWGJJJPKQSBGBMMCJSSCLPQPDXCDYYKY[CJDDYYGYWRHJRTGZNYQLDKLJSZZGZQZJGDYKSHPZMTLCPWNJAFYZDJCNMWESCYGLBTZCGMSSLLYXQSXSBSJSBBSGGHFJLYPMZJNLYYWDQSHZXTYYWHMZYHYWDBXBTLMSYYYFSXJC[DXXLHJHF[SXZQHFZMZCZTQCXZXRTTDJHNNYZQQMNQDMMG[YDXMJGDHCDYZBFFALLZTDLTFXMXQZDNGWQDBDCZJDXBZGS")
_T("QQDDJCMBKZFFXMKDMDSYYSZCMLJDSYNSBRSKMKMPCKLGDBQTFZSWTFGGLYPLLJZHGJ[GYPZLTCSMCNBTJBQFKTHBYZGKPBBYMTDSSXTBNPDKLEYCJNYDDYKZDDHQHSDZSCTARLLTKZLGECLLKJLQJAQNBDKKGHPJTZQKSECSHALQFMMGJNLYJBBTMLYZXDCJPLDLPCQDHZYCBZSCZBZMSLJFLKRZJSNFRGJHXPDHYJYBZGDLQCSEZGXLBLGYXTWMABCHECMWYJYZLLJJYHLG[DJLSLYGKDZPZXJ")
_T("YYZLWCXSZFGWYYDLYHCLJSCMBJHBLYZLYCBLYDPDQYSXQZBYTDKYXJY[CNRJMPDJGKLCLJBCTBJDDBBLBLCZQRPPXJCJLZCSHLTOLJNMDDDLNGKAQHQHJGYKHEZNMSHRP[QQJCHGMFPRXHJGDYCHGHLYRZQLCYQJNZSQTKQJYMSZSWLCFQQQXYFGGYPTQWLMCRNFKKFSYYLQBMQAMMMYXCTPSHCPTXXZZSMPHPSHMCLMLDQFYQXSZYYDYJZZHQPDSZGLSTJBCKBXYQZJSGPSXQZQZRQTBDKYXZK")
_T("HHGFLBCSMDLDGDZDBLZYYCXNNCSYBZBFGLZZXSWMSCCMQNJQSBDQSJTXXMBLTXZCLZSHZCXRQJGJYLXZFJPHYMZQQYDFQJJLZZNZJCDGZYGCTXMZYSCTLKPHTXHTLBJXJLXSCDQXCBBTJFQZFSLTJBTKQBXXJJLJCHCZDBZJDCZJDCPRNPQCJPFCZLCLZXZDMXMPHJSGZGSZZQLYLWTJPFSYASMCJBTZKYCWMYTCSJJLJCQLWZMALBXYFBPNLSFHTGJWEJJXXGLLJSTGSHJQLZFKCGNNNSZFDEQ")
_T("FHBSAQTGYLBXMMYGSZLDYDQMJJRGBJTKGDHGKBLQKBDMBYLXWCXYTTYBKMRTJZXQJBHLMHMJJZMQASLDCYXYQDLQCAFYWYXQHZ");
size_t nSecondSecTable = _tcslen(psz2ndSecTbl);
if (nCode > 1600 && nCode < 5590) //一级汉字 16-55区
{
for (int j = 22; j >= 0; j--)
{
if (nCode >= ari1stSecPosValue[j])
{
strLetter = psz1stLetter[j];
break;
}
}
}
else //二级汉字 56-87区,通过查表获取拼音首字母
{
//根据区位码确定拼音首字母
int iHiDig = nCode / 100;
int iLoDig = nCode % 100;
nCode = (iHiDig - 56) * 94 + iLoDig - 1;
if (nCode >= 0 && nCode < nSecondSecTable) //其他生僻字不再处理
strLetter = psz2ndSecTbl[nCode];
}
}