gb2312简介与转码
虽然unicode编码大势所趋,但可能仍要维护使用GBK等其他编码的老系统,这里对GB2312编码做简单介绍,并结合工作上遇到的问题给出解决办法。
GB2312简介
GB2312编码是第一个汉字编码国家标准,由中国国家标准局于1980年发布,1981年5月1日开始使用,共收录6763个汉字,其中一级汉字3755个,二级汉字3008个,另外还收录了包括拉丁字母、希腊字母、日文平假名及片假名字母、俄语西里尔字母在内的682个全角字符。
GB2312编码对所收录字符进行分区处理,共94个区,每个区有94个位,共8836个码位,这种表示方式也称为区位码,其中:
- 01-09区收录除汉字外的682个字符
- 10-15区为空白区,没使用
- 16-55区收录3755个一级汉字,按拼音排序
- 56-87区收录3008个二级汉字,按部首排序
- 88-94区为空白区,没使用
GB2312的编码范围是A1A1~FEFE,其中汉字编码范围是B0A1~F7FE,编码表可参考这里。
转成UTF-8编码
给出一个可能带有中文字符的串,要求按utf8编码输出,如何实现?考虑到常用的汉字编码方式有GB2312,GBK,GB18030和UTF-8,并且由于GB2312对于日常使用来说已经足够,这里假定输入串的编码方式只可能是GB2312或者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;
}