UTF8转GB2312
由于工作中要适配佳博58系列的热敏打印机,而目前开发驱动打印的产品采用的字符编码格式是UTF8,而查看手册时发现该打印机采用的是GB2312格式编码的,若UTF8编码格式直接打印输出中文或全角符号,则是一堆乱码,于是需要转换格式,网上可用的开源库有libiconv。移植过后发现该库对于我的产品来说略显大。而我只需要UTF8转GB2312,于是准备自己动手。
链接:https://ftp.gnu.org/pub/gnu/libiconv/libiconv-1.15.tar.gz 移植过程网上自行百度。
知识点:
- UTF8是UNICODE的子集,因此它们之间是有着对应关系
- UNICODE与GB2312并无对应关系,因此需要建立对应关系表
- UTF8格式属于变长编码格式长度1-6字节,GB2312属于定长编码格式1-2字节(其中1字节区属于ASCII编码区域)
UTF8与UNICODE之间的关系如下:
U-00000000 - U-0000007F: 0xxxxxxx
U-00000080 - U-000007FF: 110xxxxx 10xxxxxx
U-00000800 - U-0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx
U-00010000 - U-001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
U-00200000 - U-03FFFFFF: 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
U-04000000 - U-7FFFFFFF: 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
首字节有几个1就代表此UTF8格式编码是由几个字节编码的。(若是首字节是0则代表为ASCII码)
转换过程核心点在于此。
因此判断有几个1则使用以下代码:
int GetUtf8ChNum(unsigned char first)
{
unsigned char tmp = 0x80;
int count = 0;
while(tmp & first)
{
count++;
tmp = (tmp >> 1);
}
return count;
}
查表函数如下(经典二分法,速度非常块)
#define GB2312CodeSize 21791//GB2312编码此大小固定值
unsigned short SearchCode(unsigned short unicodeVal)
{
int first = 0;
int end = GB2312CodeSize - 1;
int mid = 0;
while(first <= end)
{
mid = (first + end) / 2;
if(code_table[mid].unicode == unicodeVal)
{
return code_table[mid].gb;
}
else if(code_table[mid].unicode > unicodeVal)
{
end = mid - 1;
}
else
{
first = mid + 1;
}
}
return 0;
}
UTF8最大有6个字节编码,GB2312最大2个字节编码,而GB2312在UTF8所处的编码区间是3个字节的区间因此就根据返回值处理3个字节和1个字节的UTF8的编码即可。
void Utf8ToGb2312(const unsigned char* utf8, unsigned char *gb2312, int len)
{
int i = 0;
int j = 0;
int Count = 0;
u16 unicodeVal = 0;
u16 gb2312Val = 0;
//循环查表找出对应GB2312码
while(i < len)
{
switch(GetUtf8ChNum(utf8[i]))
{
case 0:
gb2312[j] = utf8[i];
Count = 1;
break;
case 2:
gb2312[j] = utf8[i];
gb2312[j + 1] = utf8[i + 1];
Count = 2;
break;
case 3://UTF8->Unicode
gb2312[j + 1] = ((utf8[i] & 0x0F) << 4) | ((utf8[i + 1] >> 2) & 0x0F);
gb2312[j] = ((utf8[i + 1] & 0x03) << 6) + (utf8[i + 2] & 0x3F);
memcpy(&unicodeVal, (gb2312 + j), 2);//取Unicode值
gb2312Val = SearchCode(unicodeVal);//根据unicodeVal值查表取得对应的GB2312值
if (gb2312Val != 0)//gb2312有值表示查到表中对应编码,转换大小端以适应我的设备
{
gb2312Val = (gb2312Val >> 8) | (gb2312Val << 8);
memcpy((gb2312 + j), &gbKey, 2);
}
Count = 3;
break;
case 4:
Count = 4;
break;
case 5:
Count = 5;
break;
case 6:
Count = 6;
break;
default:
printf("Unknown code, len > 6\n");
break;
}
i += Count;
if (Count == 1)
{
j++;
}
else
{
j += 2;
}
}
}
附录:表的格式,本次是作为头文件包含。
网上的格式是这样:
unicode GB2312
00A4 A1E8
00A7 A1EC
00A8 A1A7
00B0 A1E3
............
此处省略N行
............
FFE0 A1E9
FFE1 A1EA
FFE3 A3FE
FFE5 A3A4
链接http://blog.csdn.net/longronglin/article/details/1355890
头文件中排序之后是这样:
typedef struct unicode_gb
{
unsigned short unicode;
unsigned short gb;
} UNICODE_GB;
UNICODE_GB code_table[] = {
{0x00A4,0xA1E8},
{0x00A7,0xA1EC},
{0x00A8,0xA1A7},
......
此处省略N行
......
{0xFFE2,0xA956},
{0xFFE3,0xA3FE},
{0xFFE4,0xA957},
{0xFFE5,0xA3A4}
};
源文件大小:
再编译成库,大小感人:
再看看libiconv-1.15.tar.gz编译出来的库大小:
一个1.3M
验证库:
自此打印机可以肆无忌惮的打印中文啦~~由于调试,金额可以随便指定,只是个样子而已啦!
若要源码,以及对照表,则从此处下载,发布在我的资源中
链接:http://download.csdn.net/download/skyblue535/9991713