2011年5月3日星期二

关于UTF-8编码的使用

编码转换的话,最通用的工具算是iconv/libiconv,可以当命令行工具使用,也可以当C语言库使用。在C语言中有两种类型来存放字符,通常char用来表示单个ASCII字符,用wchar_t来表示宽字符(可以是Unicode),字符串用'\0'表示终止。其实语言标准只是规定这两种类型的长度,其中char是8-bits,而wchar比char长,至于具体的数据所使用的编码没有固定。不过把wchar[]放在char[]中会导致连续8bits的0会让已有代码误以为字符串已经结束,并且统计字符个数时可以直接使用wchar[]的长度(例如中文的一个汉字和英文的一个字母都占一个wchar)。在8bits的char中ASCII编码占用低7bits,中文gb/gbk编码的做法是使用最高位做标记,每个中文字符占两个char。如果要转换为unicode编码的话,可以使用建立好的映射表。UTF-8是Unicode字符的一种编码方式,当最高位为0时和ASCII编码一致,否则最高位为1的个数表示这个字符将占几个char来储存。这样一来UTF-8编码的字符用char[]来存放,当仅使用ASCII字符时与ASCII编码一致,相比wchar每个字符都占用的等长的空间要节约空间。在使用的时候,UTF-8编码的char[]可以直接转换为wchar_t[]的宽字符,之后按照宽字符处理,某些系统会提供ASCII的接口和宽字符的接口两个版本。不过,最简单的做法是,只把字符编码在IO时处理,外部用ASCII和UTF-8来储存,然后统一读取为Unicode来处理。比如Java和Python3都是内置了这样的做法。

常用的简体中文编码GB2312/GBK以及后续兼容标准所属的代码页为CP936,使用如下命令可以把CP936转换为UTF-8的编码格式:
iconv -f CP936 -t UTF-8 < input.txt > output.txt
具体的,命令行用法详见"man iconv",函数库用法见"man 3 iconv"。

在iconv不可用的情况下,UTF-8转UNICODE可以很直接的实现,以下代码摘自SDL_ttf:
/*
SDL_ttf: A companion library to SDL for working with TrueType (tm) fonts
Copyright (C) 1997-2004 Sam Lantinga
*/
static Uint16 *LATIN1_to_UNICODE(Uint16 *unicode, const char *text, int len)
{
int i;
for ( i=0; i < len; ++i ) {
unicode[i] = ((const unsigned char *)text)[i];
}
unicode[i] = 0;
return unicode;
}

static Uint16 *UTF8_to_UNICODE(Uint16 *unicode, const char *utf8, int len)
{
int i, j;
Uint16 ch;
for ( i=0, j=0; i < len; ++i, ++j ) {
ch = ((const unsigned char *)utf8)[i];
if ( ch >= 0xF0 ) {
ch = (Uint16)(utf8[i]&0x07) << 18;
ch |= (Uint16)(utf8[++i]&0x3F) << 12;
ch |= (Uint16)(utf8[++i]&0x3F) << 6;
ch |= (Uint16)(utf8[++i]&0x3F);
} else
if ( ch >= 0xE0 ) {
ch = (Uint16)(utf8[i]&0x0F) << 12;
ch |= (Uint16)(utf8[++i]&0x3F) << 6;
ch |= (Uint16)(utf8[++i]&0x3F);
} else
if ( ch >= 0xC0 ) {
ch = (Uint16)(utf8[i]&0x1F) << 6;
ch |= (Uint16)(utf8[++i]&0x3F);
}
unicode[j] = ch;
}
unicode[j] = 0;
return unicode;
}

其他语言中的实现,例如OCaml中的,可以参考如第三方下库:
http://ocaml-extlib.googlecode.com/svn/doc/apiref/UTF8.html

上面已经说过用UTF-8来储存并读取为Unicode来处理,我们现在所需要的只是这两个编码间的互相转换就可以了,并注意Unicode编码还有一个字节序的问题。

没有评论: