GBK转unicode码查询表的改进

上篇文章《GBK转unicode码查询表》中有一个美中不足的地方,就是占用的内存空间稍大了一点,约128KB,当然,对于PC这种平台无所谓,但对于一些场合来说,能省内存就省内存,何况那个表省内存又是件很easy的事情。

首先说明,平时使用GBK汉字编码字符集就够用了。GB18030向下兼容GBK,所以在文章、代码直接称GBK了。对于这些标准细则,有兴趣的可以深究一下。

再说说修改的地方,其实,经过观察生成的查询表,就发现,前面128个字符就是ASCII码,完全不用去查询表(实际代码中也没有去查),ASCII码后面有N个unicde码是0x00,只占地方,完全无用。所以,只要找到unicode非零的最低的GBK码即可。经多方查证(包括研究上篇文章给出的文本文件以及百度搜索),发现有用的最小的GBK码是0x8140。这个是什么字符,我不太懂,可参考地址:http://ff.163.com/newflyff/gbk-list/。比这个值小的都没有在GBK编码中找到。因此,我们的表中只需要存储这个值以后的GBK对应的unicode编码即可。这样一来,就可以节省一半的空间了(实际占用62.9KB)。在查询函数中,将字符的GBK码减去0x8140即可得到查询表中正确的unicode码。

上述方法不复杂,也不难想到。与我之前存储ASCII字符方法一样,即不去存储95个ASCII码,而只存储从0x20开始的ASCII码,在查询时,将传入的字符减去0x20即可。

下面给出修改后的程序(修改地方不多,一个是存储查询表时,从0x8140开始,另一个是在查询过程中将GBK减去0x8140)。

生效查询表程序代码如下:

#include <stdio .h>
#include <stdlib .h>
#include <string .h>

#define ARRAY  "gbkuni30"
#define MAX_LEN  65535    // 2字节最大数
static  unsigned  short big_buffer[MAX_LEN] = { 0};

// 源文件
#define SRC  "gbkuni30.txt"

// 生产的头文件
#define DST  "gbkuni30_gen.h"

int make_charmap_gb18030()
{
     char buffer[ 16] = { 0};
     char* p = NULL;
    FILE* fp_c = NULL;
    FILE* fp = NULL;
     int len =  0;
     int x1 =  0;
     int x2 =  0;
     int i =  0;
     int max_num =  0;
     int cnt =  0;

    fp = fopen(SRC,  "r");
     if (fp == NULL)
    {
        printf( "open file error!!\n");
         return - 1;
    }

    fseek(fp,  0, SEEK_END);
    len = ftell(fp);
    fseek(fp, 0,SEEK_SET);

    printf( "file len: %d\n", len);

    fp_c = fopen(DST,  "w+");
     if (fp_c == NULL)
    {
        printf( "open file error!!\n");
         return - 1;
    }

    fprintf(fp_c,  "/**********************************************************************************/\n");
    fprintf(fp_c,  "/*             GBK(GB18030) to UNICODE table, powered by Late Lee                 */\n");
    fprintf(fp_c,  "/*                        http://www.latelee.org                                  */\n");
    fprintf(fp_c,  "/*                         %s %s                                   */\n", __DATE__, __TIME__);
    fprintf(fp_c,  "/* The source file comes from:                                                    */\n");
    fprintf(fp_c,  "/* http://icu-project.org/repos/icu/data/trunk/charset/source/gb18030/gbkuni30.txt*/\n");
    
    fprintf(fp_c,  "/**********************************************************************************/\n");

    fprintf(fp_c,  "#ifndef __GBK2UNICODE__H\n");
    fprintf(fp_c,  "#define __GBK2UNICODE__H\n\n");

    fprintf(fp_c,  "");
    fprintf(fp_c,  "static unsigned short %s[] = \n{\n", ARRAY);

     // 先读取到缓冲区,解析出两个数
     while (fgets(buffer,  32, fp) != NULL)
    {
        sscanf(buffer,  "%x:%x\n", &x1, &x2);
        
         //printf("%s", buffer);
         //printf("%04x %x\n", x1, x2);
         //fprintf(fp_c, "0x%04x, 0x%x,\n", x1, x2);
        big_buffer[x2] = x1;
         if (x2 > max_num)
            max_num = x2;

    }

    printf( "max num: %d %x\n", max_num, max_num);
     // 注:为节省存储空间,从0x8140开始存储,查询时需要减去此数
     for (i =  0x8140; i < max_num +  1; i++)
    {
         //printf("0x%04x\n", big_buffer[i]);
        fprintf(fp_c,  "0x%04x, ", big_buffer[i]);
        cnt++;
         if (cnt %  10 ==  0)
        {
            fprintf(fp_c,  "    // line num %d \n", cnt /  10 -  1);
        }
    }
    fprintf(fp_c,  "\n");
    fprintf(fp_c,  "};\n\n");
    fprintf(fp_c,  "#endif //__GBK2UNICODE__H\n");
    fprintf(stdout,  "Job done!\n");

    fclose(fp);
    fclose(fp_c);

     return  0;

}

 

转换查询函数如下:

/**
 * gbk字符串转unicode字符串
 *
 * @param unicode  unicode字符串
 * @param gb       gbk字符串
 * @param len      gbk字符串长度
 *
 * @return 返回转换后的unicode字符串长度(1个字符占2字节)
 *
 * @note 
 *         1. 所有字符的unicode编码为2字节??
 *         2. 必须使用unsigned char进行移位计算,否则结果不正确
 */

int gbk_to_unicode( unsigned  short  int* unicode,  const  char* gb,  int len)
{
     int i,j;
    i =  0;
     unsigned  char* gb_temp = ( unsigned  char *)gb;    // 必须转换成无符号

     for(j =  0; i < len; j++)
    {
         if (gb_temp[i] <=  0x80)
        {
            unicode[j] = gb_temp[i];
            i++;
        }
         else
        {
             unsigned  short  int temp;
             //printf("%x %x\n", gb_temp[i], gb_temp[i+1]);
            temp = (gb_temp[i] <<  8) + gb_temp[i+ 1];
            temp -=  0x8140// 查询表从0x8140开始存储,故减去此数
            unicode[j] = gbkuni30[temp];
            i +=  2;
             //printf("debug: gbk: 0x%x unicode: 0x%x\n", temp+0x8140, unicode[j]);
        }
    }

     return j;
}

 

对于代码优化,我还有比较注意的,举个例子,有个接口需要修改RGB颜色值,要是我设计的话,肯定将RGB三个颜色值合成一个int型参数。但我看的代码中,是将三个值分开传递的,这样,无端多了几个参数。经询问,得到的解释是:用户可读性强。对此,我不作评论。在公司层面上讲,我绝对服从组织上的决定。

 

记于文后:

对于汉字及编码,很早我就开始搞了,只是没时间研究那么深。写了现在这篇文章后,估计不会在这方面再作研究了。再研究下去也没多大意思。

 

                                                                                      迟,记于2013年8月24日晚上

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值