GBK转unicode码查询表的改进 | 迟思堂工作室
A-A+

GBK转unicode码查询表的改进

2014-09-08 11:35 GNU/Linux程序 暂无评论 阅读 1,154 次

上篇文章《GBK转unicode码查询表》中有一个美中不足的地方,就是占用的内存空间稍大了一点,约128KB,当然,对于PC这种平台无所谓,但对于一些场合来说,能省内存就省内存,何况那个表省内存又是件很easy的事情。
首先说明,平时使用GBK汉字编码字符集就够用了。GB18030向下兼容GBK,所以在文章、代码直接称GBK了。对于这些标准细则,有兴趣的可以深究一下。
再说说修改的地方,其实,经过观察生成的查询表,就发现,前面128个字符就是ASCII码,完全不用去查询表(实际代码中也没有去查),ASCII码后面有N个unicde码是0x00,只占地方,完全无用。所以,只要找到unicode非零的最低的GBK码即可。经多方查证(包括研究上篇文章给出的文本文件以及百度搜索),发现有用的最小的GBK码是0x8140。这个是什么字符,我不太懂,可参考地址:。比这个值小的都没有在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, "/* */\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日晚上



如果本文对阁下有帮助,不妨赞助笔者以输出更多好文章,谢谢!
donate




给我留言