UTF-8
互联网的普及, 强烈要求出现一种统一的编码方式. UTF-8就是在互联网上使用最广的一种unicode的实现方式. 其他实现方式还包括UTF-16和UTF-32, 不过在互联网上基本不用.
重复一遍, 这里的关系是, UTF-8是Unicode的实现方式之一.
UTF-8最大的一个特点, 就是它是一种变长的编码方式. 它可以使用1~6个字节表示一个符号, 根据不同的符号而变化字节长度.
UTF-8的编码规则
UTF-8的编码规则很简单, 只有两条:
1) 对于单字节的符号, 字节的第一位设为0, 后面7位为这个符号的unicode码. 因此对于英语字母, UTF-8编码和ASCII码是相同的.
2) 对于n字节的符号(n>1), 第一个字节的前n位都设为1, 第n+1位设为0, 后面字节的前两位一律设为10. 剩下的没有提及的二进制位, 全部为这个符号的unicode码.
如果你对 UTF-8 编码不是非常了解,就不要试图在 C 程序中徒手处理 UTF-8 文本。如果你对 UTF-8 非常了解,就更没必要这样做。找一个提供了 UTF-8 文本处理功能并且可以跨平台运行的 C 库来做这件事吧!
GLib 就是这样的库。
从问题出发
下面的这段文本是 UTF-8 编码的(我之所以如此确定,是因为我用的是 Linux 系统,系统默认的文本编码是 UTF-8):
我的 C81 每天都在口袋里
@
我需要在 C 程序中读入这些文本。在读到 '@' 字符时,我需要判定 '@' 左侧与之处于同一行的文本是否都是空白字符。
简单起见,我忽略了文件读取的过程,将上述文本表示为 C 字符串:
gchar *demo_text =
"我的 C81 每天都在口袋里\n"
" @";
注:在 GLib 中,gchar 就是 char,即 typedef char gchar;
下文,当我说『demo_text 字符串』时,指的是以 demo_text 指针的值为基地址的 strlen(demo_text) + 1 个字节的内存空间,这是 C 语言字符串的基本常识。
UTF-8 文本长度与字符定位
为了模拟程序读到 '@' 字符这一时刻,我需要用一个 char * 类型的指针对 demo_text 字符串中的 '@' 字符进行定位。
'@' 字符在 demo_text 的末尾。我需要一个偏移距离,而这个偏移距离就是 demo_text 字串在 UTF-8 编码层次上的长度,通过这个偏移距离,我可以从 demo_text 字符串的基地址跳到 '@' 字符的基地址。
GLib 提供了 g_utf8_strlen 函数计算 UTF-8 字符串长度,因此我可以得到从 demo_text 字串的基地址到 '@' 字符基地址的偏移距离:
glong offset = g_utf8_strlen(demo_text, -1);
结果是 38,恰好是 demo_text 字符串在 UTF-8 编码层次上的长度(不含字串结尾的 null 字符,亦即 '\0' 字符)。
g_utf8_strlen 的原型如下:
glong g_utf8_strlen(const gchar *p, gssize max);
注:glong 即 long,而 gssize 即 signed long。
g_utf8_strlen 第二个参数 max 的设定规则如下:
如果它是负数,那么就假定字符串是以 null 结尾的(这是 C 字符串常识),然后统计 UTF-8 字符的个数。
如果它为 0,就是不检测字符串长度……这个值纯粹是出来打酱油的。
如果它为正数,表示的是字节数。g_utf8_strlen 会按照字节数从字符串中截取字节,然后再统计所截取的字节对应的 UTF-8 字符的个数。
有了偏移距离,就可以在 demo_text 中定位 '@' 字符了,即: