红联Linux门户
Linux帮助

FreeBSD下宽字节与多字节互相转换的函数支持

发布时间:2007-04-09 00:27:00来源:红联作者:Ptosassy
在FreeBSD下,对多字节语言编码的处理是通过“多字节字符(mb)与宽字节字符(wc)的互相转换”来提供支持的。作为系统内部,Freebsd在libc函数级别上对“宽字节wc“提供了很好的支持,所有宽字节处理函数均定义在“/usr/src/include/wchar.h“中。
FreeBSD中对多字节语言编码进行支持的基本原理是:
1、提供了统一的处理机制,就是宽字节处理,这样就避免出现“半个字”的问题,所有的字符都是作为一个单独的对象(宽字节字符)来处理。在freebsd系统中,宽字节字符定义为4个字节,可以容纳Unicode的所有字符。在libc中,Freebsd提供了以下函数,来支持wc<=>mb
mb -> wc
字符处理: mbtowc mbrtowc
字符串处理: mbstowcs mbsrtowcs mbsnrtowcs

wc -> mb
字符处理:wctomb wcrtomb
字符串处理:wcstombs wcsrtombs wcsnrtombs

通过这些函数,可以实现系统底层对多字节语言编码的处理,大致的流程是:
多字节字符首先要转换成宽字节字符,然后由系统提供的宽字节字符、字符串处理函数进行处理,诸如排序、比较、输出。根据需要再把处理的结果通过wc -> mb的函数进行转换,转换成特定的多字符编码格式。
2、对特定多字符编码格式的绑定:
对于汉字处理来说,存在多种编码格式,诸如UTF-8、GB2312、GBK、GB18030等等,由于编码方式的不同,在进行wc<->mb转换的时候,就需要不同的转换规则对其进行支持。这种绑定从原理上说是通过locale的LC_CTYPE来实现的。
具体的实现,是在/usr/src/lib/libc/locale目录下,原理是当应用程序调用setlocale函数来设定locale时,系统会对LC_CTYPE进行检查,并根据LC_CTYPE的值,对wc<->mb处理函数进行切换。例如:
阅读mbrtowc函数(mbrtowc.c)
size_t
mbrtowc(wchar_t * __restrict pwc, const char * __restrict s,
size_t n, mbstate_t * __restrict ps)
{
static mbstate_t mbs;
if (ps == NULL)
ps = &mbs;
return (__mbrtowc(pwc, s, n, ps));
}

它最终调用的是__mbrtowc(),__mbrtowc是一个函数指针,在setrunelocale.c中,这个指针会根据系统当前的LC_CTYPE进行初始化,如果当前的LC_CTYPE=zh_CN.GB18030,系统会转向执行GB18030.c中的_GB18030_init()函数,在这个函数里面,这个函数指针会指向GB18030.c中的_GB18030_mbrtowc()函数:
int
_GB18030_init(_RuneLocale *rl)
{
__mbrtowc = _GB18030_mbrtowc;
__wcrtomb = _GB18030_wcrtomb;
__mbsinit = _GB18030_mbsinit;
_CurrentRuneLocale = rl;
__mb_cur_max = 4;
return (0);
}

而_GB18030_mbrtowc()函数具体实现了GB18030编码的字符到宽字节字符的转换:
static size_t
_GB18030_mbrtowc(wchar_t * __restrict pwc, const char * __restrict s,
size_t n, mbstate_t * __restrict ps)
{
_GB18030State *gs;
wchar_t wch;
int ch, len, ocount;
size_t ncopy;
gs = (_GB18030State *)ps;
if (gs->count < 0 || gs->count > sizeof(gs->bytes)) {
errno = EINVAL;
return ((size_t)-1);
}
if (s == NULL) {
s = "";
n = 1;
pwc = NULL;
}
ncopy = MIN(MIN(n, MB_CUR_MAX), sizeof(gs->bytes) - gs->count);
memcpy(gs->bytes + gs->count, s, ncopy);
ocount = gs->count;
gs->count += ncopy;
s = (char *)gs->bytes;
n = gs->count;
if (n == 0)
/* Incomplete multibyte sequence */
return ((size_t)-2);
/*
* Single byte: [00-7f]
* Two byte: [81-fe][40-7e,80-fe]
* Four byte: [81-fe][30-39][81-fe][30-39]
*/
ch = (unsigned char)*s++;
if (ch <= 0x7f) {
len = 1;
wch = ch;
} else if (ch >= 0x81 && ch <= 0xfe) {
wch = ch;
if (n < 2)
return ((size_t)-2);
ch = (unsigned char)*s++;
if ((ch >= 0x40 && ch <= 0x7e) || (ch >= 0x80 && ch <= 0xfe)) {
wch = (wch << 8) | ch;
len = 2;
} else if (ch >= 0x30 && ch <= 0x39) {
/*
* Strip high bit off the wide character we will
* eventually output so that it is positive when
* cast to wint_t on 32-bit twos-complement machines.
*/
wch = ((wch & 0x7f) << 8) | ch;
if (n < 3)
return ((size_t)-2);
ch = (unsigned char)*s++;
if (ch < 0x81 || ch > 0xfe)
goto ilseq;
wch = (wch << 8) | ch;
if (n < 4)
return ((size_t)-2);
ch = (unsigned char)*s++;
if (ch < 0x30 || ch > 0x39)
goto ilseq;
wch = (wch << 8) | ch;
len = 4;
} else
goto ilseq;
} else
goto ilseq;
if (pwc != NULL)
*pwc = wch;
gs->count = 0;
return (wch == L'\0' ? 0 : len - ocount);
ilseq:
errno = EILSEQ;
return ((size_t)-1);
}

Freebsd这种利用Locale的绑定机制,可以非常灵活的对多种多字节编码字符处理提供支持,我们可以把如下这几个函数指针当成一个接口,如果要实现对某种多字节编码的支持,只要实现这几个函数的实现即可。这种机制很好的体现了Unix系统对I18n的支持,下面是UTF_8的绑定:
__mbrtowc = _UTF8_mbrtowc;
__wcrtomb = _UTF8_wcrtomb;
__mbsinit = _UTF8_mbsinit;
__mbsnrtowcs = _UTF8_mbsnrtowcs;
__wcsnrtombs = _UTF8_wcsnrtombs;

具体的代码都是在/usr/src/lib/lib/locale目录下
GB18030.c euc.c utf-8.c gb2312.c等等,分别对几种汉字的编码方案提供支持。
由此可见,Freebsd通过locale机制对汉字处理的支持,还是很充分的,也可以看出,直接影响Freebsd对中文字符处理的locale是LC_CTYPE。
文章评论

共有 0 条评论