gb2312简介与转码

虽然unicode编码大势所趋,但可能仍要维护使用GBK等其他编码的老系统,这里对GB2312编码做简单介绍,并结合工作上遇到的问题给出解决办法。

GB2312简介

GB2312编码是第一个汉字编码国家标准,由中国国家标准局于1980年发布,1981年5月1日开始使用,共收录6763个汉字,其中一级汉字3755个,二级汉字3008个,另外还收录了包括拉丁字母、希腊字母、日文平假名及片假名字母、俄语西里尔字母在内的682个全角字符。

GB2312编码对所收录字符进行分区处理,共94个区,每个区有94个位,共8836个码位,这种表示方式也称为区位码,其中:

GB2312的编码范围是A1A1~FEFE,其中汉字编码范围是B0A1~F7FE,编码表可参考这里

转成UTF-8编码

给出一个可能带有中文字符的串,要求按utf8编码输出,如何实现?考虑到常用的汉字编码方式有GB2312,GBK,GB18030和UTF-8,并且由于GB2312对于日常使用来说已经足够,这里假定输入串的编码方式只可能是GB2312或者UTF-8。

可以将问题拆成两个子问题:

如果这两个问题都能解决,那逻辑就简单了:先检查串是否为GB2312串,如果不是,则直接返回原串;否则将其做GB2312到UTF-8的编码转换。

第1个问题可以根据GB2312汉字的编码范围来判断。

bool IsGB2312(const char *str) {
    if (!str || !str[0]) return false;
    for (size_t i = 0; str[i]; i++) {
        unsigned char hi = (unsigned char)str[i];
        if (hi < 0xB0 || hi > 0xF7) continue;
        if (str[i+1]) {
            unsigned char lo = (unsigned char)str[i+1];
            if (lo < 0xA1 || lo > 0xFE)
                return false;
            i++;
        } else return false;
    }
    return true;
}

第2个问题可通过iconv系统调用来实现。

int g2u(char *str, char *buf, size_t bufsz) {
    if (IsGB2312(str)) return -1;
    size_t len = strlen(str);
    iconv_t cd = iconv_open("UTF-8", "GB2312");
    if (cd == (iconv_t)-1) return -1;
    size_t ret = iconv(cd, &str, &len, &buf, &size);
    if (ret == (size_t)-1) {
        iconv_close(cd);
        return -1;
    }
    iconv_close(cd);
    return 0;
}

现在,任意给定个GB2312串或者UTF-8串,都能返回对应的UTF-8串。

string UTF8(const char *str) {
    string ans;
    if (!str || !str[0]) return ans;
    size_t len = strlen(str);
    char s1[2*len+5], s2[2*len+5];
    memset(s1, 0, sizeof(s1));
    memset(s2, 0, sizeof(s2));
    strcpy(s1, str);
    if (g2u(s1, s2, sizeof(s2)))
        ans = str;
    else
        ans = s2;
    return ans;
}
Table of Contents