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 中定位 '@' 字符了,即:
gchar *tail = g_utf8_offset_to_pointer(demo_text, offset - 1);
此时 tail 的值便是 '@' 字符的基地址。
在 UTF-8 文本中游走
现在已经获得了 '@' 的位置,接下来就是从这个位置开始向左(也就是逆序)遍历 demo_text 字符串的其它字符。GLib 为此提供了 g_utf8_prev_char 函数:
gchar * g_utf8_prev_char(const gchar *str, const gchar *p);
借助 g_utf8_prev_char 函数可以从 str 中获得 p 之前的一个 UTF-8 字符的基地址(p 是当前 UTF-8 字符的基地址)。如果 p 与 str 相同,即 p 已经指向了字符串的基地址,那么 g_utf8_find_prev_char 会返回 NULL。
对于本文要解决的问题而言,利用这个函数,可以写出从 demo_text 中的 '@' 字符所在位置开始逆序遍历 '@' 之前的所有 UTF-8 字符的过程:
glong offset = g_utf8_strlen(demo_text, -1);
gchar *viewer = g_utf8_offset_to_pointer(demo_text, offset - 1);
while (1) {
viewer = g_utf8_prev_char(viewer);
if (viewer != demo_text) {
/* do somthing here */
} else {
break;
}
}
GLib 还提供了一个 g_utf8_next_char,它可以返回当前位置的下一个 UTF-8 字符的基地址。
提取 UTF-8 字符
虽然借助 g_utf8_prev_char 与 g_utf8_next_char 可以让指针在 UTF-8 文本中走动,但是只能将一个指针定位到某个 UTF-8 字符的基地址,如果我们想得到这个 UTF-8 字符,就不是那么容易了。
例如
viewer = g_utf8_prev_char(viewer);
此时,虽然可以将 viewer 向前移动一个 UTF-8 字符宽度的距离,到达了一个新的 UTF-8 字符的基地址,但是如果我想将这个新的 UTF-8 字符打印出来,像下面这样做肯定是不行的:
g_print("%s", viewer);
注:g_print 函数与 C 标准库中的 printf 函数功能基本等价,只不过 g_print 可以借助 g_set_print_handler 函数实现输出的『重定向』。
因为 g_print 要通过 viewer 打印单个 UTF-8 字符,前提是这个 UTF-8 字符之后需要有个 '
