制作UTF8转GB2312转换库

来源:互联网 发布:张予曦淘宝店的模特 编辑:程序博客网 时间:2024/06/08 08:54

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 GB231200A4     A1E800A7     A1EC00A8     A1A700B0     A1E3............此处省略N行............FFE0     A1E9FFE1     A1EAFFE3     A3FEFFE5     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