关于在C程序中处理UTF-8文本的方法详解

2020-01-06 18:06:21王旭

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 字符之后需要有个 '',这样就是将一个 UTF-8 字符作为一个普通的 C 字符串打印了出来。这个 UTF-8 字符后面不可能有 '',除非它是 demo_text 字符串中的最后一个字符。