制作UTF8转GB2312转换库

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

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

skyblue535

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值