之前写了两篇文章
单片机—ESP8266模块开发单片机—ESP8266中oled中文显示(完美方案)
背景
今天在测试过程中发现了中文标点存在问题。
在oled中文显示中,我们用到了一个查表法进行utf-8转gbk的方式,经过实验,发现中文能够翻译出来,但是一些全角的标点,例如中文的逗号,句号,感叹号等,翻译的不正确。
这样会就导致在显示中文标点的时候,会死机或者乱码。
问题解决
首先,我们要明确HZK库中的点阵数据是全的,这是一个完整的字库,所以,现在原因就出在这个函数当中
int SwitchToGbk(const unsigned char* pszBufIn, int nBufInLen, unsigned char* pszBufOut, int* pnBufOutLen)
{
int i = 0;
int j = 0, nLen;
unsigned short unicode;
unsigned short gbk;
for(; i < nBufInLen; i++, j++)
{
if((pszBufIn[i] & 0x80) == 0x00)
{
nLen = 1;
pszBufOut[j]= pszBufIn[i];
}
/*else if((pszBufIn[i] & 0xE0) == 0xC0)
{
nLen = 2;
unicode = (pszBufIn[i] & 0x1F << 6) | (pszBufIn[i+1]& 0x3F);
}*/
else if ((pszBufIn[i] & 0xF0) == 0xE0)
{
if (i+ 2 >= nBufInLen) return -1;
unicode = (((int)(pszBufIn[i] & 0x0F)) << 12) | (((int)(pszBufIn[i+1] & 0x3F)) << 6) | (pszBufIn[i+2] & 0x3F);
gbk = mb_uni2gb_table[unicode-0x4e00];
pszBufOut[j]= gbk/256;
pszBufOut[j+1] = gbk%256;
j++;
i+=2;
}
else
{
return -1;
}
}
*pnBufOutLen = j;
fprintf(stderr,"pnbufoutlen=%d\n",*pnBufOutLen);
return 0;
}
其中,utf-8转化为unicode的时候,是没有问题的,但是这个unicode,在这个代码中是存在范围限制的,因为这个全局数组mb_uni2gb_table有个长度,所以unicode必须有这个限制。
((unicode>0x4e00)&&(unicode<0x9fa6))
而我们通过转化一个中文标点,比如“,”,发现它的unicode已经超过这个范围了,也就是这个查表法查询的表,并不完整……
那我们就需要主动一点,将它翻译不了的范围之外的数字,做一个对照翻译,例如,我们计算逗号的unicode值为0xff0c
逗号的gbk编码是0xa3ac
那么我们就主动给它返回a3ac,然后在查找HZK的时候,就不会出问题了。
因此修改了版本代码如下。(在表的结尾,补充了中文标点)
//补充中文标点
0xa1ef,
0xa3ba,0xa3ac,0xa1a3,0xa1a2,//冒号逗号顿号
0xa1b6,0xa1b7,//书名号
0xa3bf,0xa3a1,//问号感叹号
0xa1b0,0xa1b1,//双引号
0xa3a8,0xa3a9,//括号
0xa3db,0xa3dd,//中括号
0xa3bb,0xa1e6,//分号和摄氏度
0xa3a4,0xa1ad,//人民币和省略号半个
};
unsigned short chinese_code(unsigned short unicode)
{
if((unicode>0x4e00)&&(unicode<0x9fa6))
return (unicode-0x4e00);
else if(unicode == 0xff1a) //冒号
return 20903;
else if(unicode == 0xff0c) //逗号
return 20904;
else if(unicode == 0x3002) //句号
return 20905;
else if(unicode == 0x3001) //顿号
return 20906;
else if(unicode == 0x300a) //《
return 20907;
else if(unicode == 0x300b) //》
return 20908;
else if(unicode == 0xff1f) //?
return 20909;
else if(unicode == 0xff01) //!
return 20910;
else if(unicode == 0x201c) //“
return 20911;
else if(unicode == 0x201d) //”
return 20912;
else if(unicode == 0xff08) //(
return 20913;
else if(unicode == 0xff09) //)
return 20914;
else if(unicode == 0x3010) //【
return 20915;
else if(unicode == 0x3011) //】
return 20916;
else if(unicode == 0xff1b) //分号
return 20917;
else if(unicode == 0x2103) //摄氏度
return 20918;
else if(unicode == 0xffe5) //人民币
return 20919;
else if(unicode == 0x2026) //省略号
return 20920;
else
return 20902;
}
int SwitchToGbk(const unsigned char* pszBufIn, int nBufInLen, unsigned char* pszBufOut, int* pnBufOutLen)
{
int i = 0;
int j = 0, nLen;
unsigned short unicode;
unsigned short gbk;
unsigned short table_sign;
for(i=0; i < nBufInLen; i++, j++)
{
if((pszBufIn[i] & 0x80) == 0x00) //英文字符
{
nLen = 1;
pszBufOut[j]= pszBufIn[i];
}
/*
else if((pszBufIn[i] & 0xE0) == 0xC0)// 2λ
{
nLen = 2;
unicode = (pszBufIn[i] & 0x1F << 6) | (pszBufIn[i+1]& 0x3F);
}*/
else if ((pszBufIn[i] & 0xF0) == 0xE0) // 中文
{
if (i+ 2 >= nBufInLen)
return -1;
unicode = (((int)(pszBufIn[i]&0x0F))<<12)|(((int)(pszBufIn[i+1]&0x3F))<<6)|(pszBufIn[i+2]&0x3F);
//中文标点补充判断,
table_sign=chinese_code(unicode);
gbk = mb_uni2gb_table[table_sign];
pszBufOut[j]= gbk/256;
pszBufOut[j+1] = gbk%256;
j++;
i+=2;
}
else
{
return -1;
}
}
*pnBufOutLen = j;
return 0;
}
这样,就可以完美的支持中文标点符号了
如果还想添加其他符号,也可以继续扩展那个转化函数。或者说换个思路,可以补充出一个完整的转化表格用来查找,但是感觉没必要,把日常能用的文字标点补充上就可以了,没必要浪费宝贵的flash资源。
新版代码下载
utf-8转gbkc语言完美版,并支持扩展
题外话
今天是圣诞节,有人说,过什么国外的节日,那都是西方国家的,放在几年以前,我也这么想,但是现在随着年龄增大,可能想的更深刻了一些,我们虽然过洋节,但是我并不想知道这个节日的由来,也不知道它要纪念什么或者表达什么,我们只是今天,为了健康吃个苹果,为了庆祝周末,吃顿好吃的,大家一起玩,小孩子也开心,玩点新玩具,放点小烟花。经济拉动了,生活幸福感也提高了,至于洋鬼子的神,对不起,不认识。
今天老板找我谈话,觉得我对他有意见,我说没有,完全没有,你定的那些制度,我是绝对的赞同。
为什么这么说,其实现实就是这样,所有人(除了老板自己)没有一个没有意见的,但是没有任何人去提,但他们却希望有人去提,然后坐享其成。但是我也不差这点假期,何苦为难自己呢。人性啊,也很难想象当初的共产党人如何做到那么团结的。