Unicode与GBK互转

    <<Unicode与GBK互转>>
            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值.
  1. <SPAN xmlns="http://www.w3.org/1999/xhtml">// #c---  
  2. static const unsigned short tab_GBK_to_UCS2[][2] =  
  3. {  
  4.    /* GBK    Unicode     字 */  
  5.   
  6.     {0x8140, 0x4E02}, // 丂   
  7.     {0x8141, 0x4E04}, // 丄   
  8.     {0x8142, 0x4E05}, // 丅   
  9.     {0x8143, 0x4E06}, // 丆   
  10.     {0x8144, 0x4E0F}, // 丏   
  11.     ... ...  
  12.     {0x817F, 0x0001}, // XXXXX   
  13.     ... ...  
  14. };  
  15. // #c---end</SPAN>  
// #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)编码.

  1. <SPAN xmlns="http://www.w3.org/1999/xhtml">// #c---  
  2. /***************************************************************************** 
  3.  * 将一个字符的GBK编码转换成Unicode(UCS-2和UCS-4)编码. 
  4.  * 
  5.  * 参数: 
  6.  *    gbk         字符的GBK编码值 
  7.  *    ucs         指向输出缓冲区, 其保存的数据即是Unicode编码值, 
  8.  *                类型为unsigned long . 
  9.  * 
  10.  * 返回值: 
  11.  *    1. 成功则返回该字符的GBK编码所占用的字节数; 
  12.  *         对于ASCII字符返回1, 对于非ASCII中文字符返回2. 
  13.  *    2. 失败则返回0. 
  14.  * 
  15.  * 注意: 
  16.  *     1. GBK 和 Unicode 都有字节序要求; 
  17.  *        字节序分为大端(Big Endian)和小端(Little Endian)两种; 
  18.  *        在Intel处理器中采用小端法表示, 在此采用小端法表示. (低地址存低位) 
  19.  ****************************************************************************/  
  20. int enc_GBK_to_unicode_one(unsigned short gbk,  
  21.         unsigned long *ucs)  
  22. {  
  23.     assert(ucs != NULL);  
  24.   
  25.     unsigned char *p = (unsigned char *) &gbk;  
  26.     unsigned char *phibyte = p + 1;  
  27.   
  28.     if ( *phibyte < 0x80 )  
  29.     {  
  30.         *ucs = *phibyte;  
  31.         return 1;  
  32.     }  
  33.     else  
  34.     {  
  35.         if ( gbk < tab_GBK_to_UCS2[0][0] ||  
  36.                 gbk > tab_GBK_to_UCS2[NUMOF_TAB_GBK_TO_UCS2 - 1][0] )  
  37.         {  
  38.             return 0;  
  39.         }  
  40.   
  41.         *ucs = tab_GBK_to_UCS2[gbk - tab_GBK_to_UCS2[0][0]][1];  
  42.     }  
  43.   
  44.     return 2;  
  45. }  
  46.   
  47. // #c---end   
  48.   
  49.   
  50. </SPAN>  
// #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是个不错的选择.

  1. <SPAN xmlns="http://www.w3.org/1999/xhtml"><SPAN xmlns="http://www.w3.org/1999/xhtml">// #c---  
  2. /*==========================================================================* 
  3.  * @Description: 
  4.  *      初始化unicode(key)与GBK(value)的映射表tab_UCS2_to_GBK 
  5.  * 
  6.  * @Returns: 
  7.  *      成功, 返回1; 
  8.  *      失败, 返回0. 
  9.  * 
  10.  *==========================================================================*/  
  11. static int enc_stc_unicode_to_GBK_init()  
  12. {  
  13.     assert(tab_UCS2_to_GBK == NULL);  
  14.   
  15.     int  i;  
  16.     void *ret;  
  17.   
  18.     tab_UCS2_to_GBK = Table_new(21791, enc_stc_unicode_to_GBK_cmp,  
  19.             enc_stc_unicode_to_GBK_hash);  
  20.     if ( tab_UCS2_to_GBK == TABLE_ERROR )  
  21.         return 0;  
  22.   
  23.     for ( i = 0; i < NUMOF_TAB_GBK_TO_UCS2; i++ )  
  24.     {  
  25.         if ( tab_GBK_to_UCS2[i][1] == 0x0001 )  
  26.             continue;  
  27.   
  28.         unsigned long k = (unsigned long)tab_GBK_to_UCS2[i][1];  
  29.         unsigned long v = (unsigned long)tab_GBK_to_UCS2[i][0];  
  30.         ret = Table_put(tab_UCS2_to_GBK, (void*)k, (void*)v);  
  31.         if ( ret != TABLE_OK )  
  32.             return 0;  
  33.     }  
  34.   
  35.     return 1;  
  36. }  
  37.   
  38. /***************************************************************************** 
  39.  * 将一个字符的Unicode(UCS-2和UCS-4)编码转换成GBK编码. 
  40.  * 
  41.  * 参数: 
  42.  *    ucs      字符的Unicode编码值 
  43.  *    gbk      指向输出的用于存储GBK编码值的缓冲区的指针 
  44.  * 
  45.  * 返回值: 
  46.  *    1. 成功则返回该字符的GBK编码所占用的字节数; 
  47.  *         对于ASCII字符返回1, 对于非ASCII中文字符返回2. 
  48.  *    2. 失败则返回0. 
  49.  * 
  50.  * 注意: 
  51.  *     1. GKB和Unicode都有字节序要求; 
  52.  *        字节序分为大端(Big Endian)和小端(Little Endian)两种; 
  53.  *        在Intel处理器中采用小端法表示, 在此采用小端法表示. (低地址存低位) 
  54.  ****************************************************************************/  
  55. int enc_unicode_to_GBK_one(unsigned long ucs, unsigned short *gbk)  
  56. {  
  57.     assert(gbk != NULL);  
  58.   
  59.     if ( ucs < 0x80 )  
  60.     {  
  61.         *gbk = ucs;  
  62.         return 1;  
  63.     }  
  64.   
  65.     if ( tab_UCS2_to_GBK == NULL )  
  66.         if ( enc_stc_unicode_to_GBK_init() == 0 )  
  67.             return 0;  
  68.   
  69.     void *pvalue;  
  70.   
  71.     pvalue = Table_get(tab_UCS2_to_GBK, (void*)ucs);  
  72.     if ( pvalue == TABLE_NO_KEY )  
  73.         return 0;  
  74.   
  75.     *gbk = (unsigned long)pvalue;  
  76.   
  77.     return 2;  
  78. }  
  79.   
  80. // #c---end   
  81.   
  82.   
  83. </SPAN></SPAN>  
// #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




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值