<>
Tags: encoding,c
1. gb2312
规定: 一个小于127的字符的意义与原来相同, 但两个大于127的字符连在一起时, 就表示
一个汉字, 前面的一个字节(他称之为高字节)从0xA1用到 0xF7, 后面一个字节(低字节)
从0xA1到0xFE, 这样我们就可以组合出大约7000多个简体汉字了. 在这些编码里, 我们还
把数学符号,罗马希腊的 字母,日文的假名们都编进去了, 连在 ASCII 里本来就有的数字
,标点,字母都统统重新编了两个字节长的编码, 这就是常说的"全角"字符, 而原来在127
号以下的那些就叫"半角"字符了.
中国人民看到这样很不错, 于是就把这种汉字方案叫做 "GB2312". GB2312 是对 ASCII
的中文扩展.
2. GBK
但是中国的汉字太多了, 我们很快就就发现有许多人的人名没有办法在这里打出来, 特别
是某些很会麻烦别人的国家领导人. 于是我们不得不继续把 GB2312 没有用到的码位找出
来老实不客气地用上.
后来还是不够用, 于是干脆不再要求低字节一定是127号之后的内码, 只要第一个字节是
大于127就固定表示这是一个汉字的开始, 不管后面跟的是不是扩展字 符集里的内容. 结
果扩展之后的编码方案被称为 "GBK" 标准, GBK 包括了 GB2312 的所有内容, 同时又增
加了近20000个新的汉字(包括繁体字)和符号.
Note: Unicode相关的基础知识请参数上一篇博文.
3. Unicode与GBK互转
3.1 GBK --> Unicode
Unicode 与 GBK 是两个完全不样的字符编码方案, 其两者没有直接关系, 要对其进行相
互转换, 最直接最高效的方法是查表.
GBK与Unicode的映射表可以从网上下载:
http://www.gnu.org/directory/libiconv.html
显然, 只需要把下载下来的映射表用一个二维数组表示即可, tab_GBK_to_UCS2[i][0]表
示GBK编码值, tab_GBK_to_UCS2[i][1]表示Unicode值.
// #c---
static const unsigned short tab_GBK_to_UCS2[][2] =
{
/* GBK Unicode 字 */
{0x8140, 0x4E02}, // 丂
{0x8141, 0x4E04}, // 丄
{0x8142, 0x4E05}, // 丅
{0x8143, 0x4E06}, // 丆
{0x8144, 0x4E0F}, // 丏
... ...
{0x817F, 0x0001}, // XXXXX
... ...
};
// #c---end
但这有一个问题, 对于GBK编码并不是连续的, 有些编码目前是没有意义的, 如0x817F,
为了方便使用数组索引下标, 我们以把这些值也插入数组, 对应的Unicode值用一个不冲
突的值表示即可, 在这用的是0x0001. 这样对于任意的GBK编码值, 我们就可直接地利用
该数组直接找出其对应的Unicode编码值了. 起初, 我还打算用一个map来实现GBK到
Unicode 的转换, 这也只都是考虑到空间是否节省和是否高效. 对于高效, 数组当然没得
说; 用树实现的map也能达到log2; 用hash实现的map, 如果能选用效好的hash函数也是能
够达到常数级别的. 对于节省空间, 如果数据是连续的, 那是最理想不过了, 但目前这一
问题并不连续, 所以, 为了连续, 只能浪费点了, 算了下, 对于这一问题空间利用率为
69%左右; 如果是map, 因每个节点都要点空间, 所以算了一下, 其空间利用率也就67%左
右.
将一个字符的GBK编码转换成Unicode
(UCS-2和UCS-4)编码.
// #c---
/*****************************************************************************
* 将一个字符的GBK编码转换成Unicode(UCS-2和UCS-4)编码.
*
* 参数:
* gbk 字符的GBK编码值
* ucs 指向输出缓冲区, 其保存的数据即是Unicode编码值,
* 类型为unsigned long .
*
* 返回值:
* 1. 成功则返回该字符的GBK编码所占用的字节数;
* 对于ASCII字符返回1, 对于非ASCII中文字符返回2.
* 2. 失败则返回0.
*
* 注意:
* 1. GBK 和 Unicode 都有字节序要求;
* 字节序分为大端(Big Endian)和小端(Little Endian)两种;
* 在Intel处理器中采用小端法表示, 在此采用小端法表示. (低地址存低位)
****************************************************************************/
int enc_GBK_to_unicode_one(unsigned short gbk,
unsigned long *ucs)
{
assert(ucs != NULL);
unsigned char *p = (unsigned char *) &gbk;
unsigned char *phibyte = p + 1;
if ( *phibyte < 0x80 )
{
*ucs = *phibyte;
return 1;
}
else
{
if ( gbk < tab_GBK_to_UCS2[0][0] ||
gbk > tab_GBK_to_UCS2[NUMOF_TAB_GBK_TO_UCS2 - 1][0] )
{
return 0;
}
*ucs = tab_GBK_to_UCS2[gbk - tab_GBK_to_UCS2[0][0]][1];
}
return 2;
}
// #c---end
3.2 Unicode --> GBK
要实现Unicode到GBK的转换, 可以使用以上的数组表结构, 但由于GBK对应的unicode值的
范围太广, 会造成很大的浪费, 空间利用率只有30%. 无奈只能用map了.
用hash实现的map是个不错的选择.
// #c---
/*==========================================================================*
* @Description:
* 初始化unicode(key)与GBK(value)的映射表tab_UCS2_to_GBK
*
* @Returns:
* 成功, 返回1;
* 失败, 返回0.
*
*==========================================================================*/
static int enc_stc_unicode_to_GBK_init()
{
assert(tab_UCS2_to_GBK == NULL);
int i;
void *ret;
tab_UCS2_to_GBK = Table_new(21791, enc_stc_unicode_to_GBK_cmp,
enc_stc_unicode_to_GBK_hash);
if ( tab_UCS2_to_GBK == TABLE_ERROR )
return 0;
for ( i = 0; i < NUMOF_TAB_GBK_TO_UCS2; i++ )
{
if ( tab_GBK_to_UCS2[i][1] == 0x0001 )
continue;
unsigned long k = (unsigned long)tab_GBK_to_UCS2[i][1];
unsigned long v = (unsigned long)tab_GBK_to_UCS2[i][0];
ret = Table_put(tab_UCS2_to_GBK, (void*)k, (void*)v);
if ( ret != TABLE_OK )
return 0;
}
return 1;
}
/*****************************************************************************
* 将一个字符的Unicode(UCS-2和UCS-4)编码转换成GBK编码.
*
* 参数:
* ucs 字符的Unicode编码值
* gbk 指向输出的用于存储GBK编码值的缓冲区的指针
*
* 返回值:
* 1. 成功则返回该字符的GBK编码所占用的字节数;
* 对于ASCII字符返回1, 对于非ASCII中文字符返回2.
* 2. 失败则返回0.
*
* 注意:
* 1. GKB和Unicode都有字节序要求;
* 字节序分为大端(Big Endian)和小端(Little Endian)两种;
* 在Intel处理器中采用小端法表示, 在此采用小端法表示. (低地址存低位)
****************************************************************************/
int enc_unicode_to_GBK_one(unsigned long ucs, unsigned short *gbk)
{
assert(gbk != NULL);
if ( ucs < 0x80 )
{
*gbk = ucs;
return 1;
}
if ( tab_UCS2_to_GBK == NULL )
if ( enc_stc_unicode_to_GBK_init() == 0 )
return 0;
void *pvalue;
pvalue = Table_get(tab_UCS2_to_GBK, (void*)ucs);
if ( pvalue == TABLE_NO_KEY )
return 0;
*gbk = (unsigned long)pvalue;
return 2;
}
// #c---end