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

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

要解决这个问题,只能是将 viewer 所指向的 UTF-8 字符相应的字节数据提取出来,放到一个字符数组或在堆中为其创建存储空间,然后再打印这个字符数组或堆空间中的数据。例如:


gchar *new_viewer = g_utf8_next_char(viewer);

sizt_t n = new_viewer - viewer;
gchar *utf8_char = malloc(n + 1);
memcpy(utf8_char, viewer, n);
utf8_char[n] = '';
g_print("%s", utf8_char);
free(utf8_char);

这样显然太繁琐了。不过,这意味着我们应该写一个函数专门做这件事。这个函数可取名为 get_utf8_char,定义如下:


static gchar * get_utf8_char(const gchar *base) {
  gchar *new_base = g_utf8_next_char(base);
  gsize n = new_base - base;
  gchar *utf8_char = g_memdup(base, (n + 1));
  utf8_char[n] = '';
  return utf8_char;
}

借助这个函数,就可以实现从 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) {
  gchar outbuf[7] = {''};
  viewer = g_utf8_prev_char(viewer);
  if (viewer != demo_text) {
    gchar *utf8_char = get_utf8_char(viewer);
    g_print("%s", utf8_char);
    g_free(utf8_char);
  } else {
    break;
  }
}
g_print("n");

注:g_memdup 等价于 C 标准库中的 malloc + memcpy,而 g_free 则等价与 C 标准库中的 free。
空白字符比较

现在,假设给定一个 UTF-8 字符 x,怎么判断它与某个 UTF-8 字符相等?

不要忘记,所谓的一个 UTF-8 字符,本质上只不过是 char * 类型的指针引用的一段内存空间。基于这一事实,利用 C 标准库提供的 strcmp 函数即可实现 UTF-8 字符的比较。

下面,我定义了函数 is_space,用它判断一个 UTF-8 字符是否为空白字符。


static gboolean is_space(const gchar *s) {
  gboolean ret = FALSE;
  char *space_chars_set[] = {" ", "t", " "};
  size_t n = sizeof(space_chars_set) / sizeof(space_chars_set[0]);
  for (size_t i = 0; i < n; i++) {
    if (!strcmp(s, space_chars_set[i])) {
      ret = TRUE;
      break;
    }
  }
  return ret;
}

注:gboolean 是 GLib 定义的布尔类型,其值要么是 TRUE,要么是 FALSE。

在 is_space 函数中,我只是判断了三种空白字符类型——英文空格、中文全角空格以及制表符。

虽然回车符与换行符也是空白字符,但是为了解决这篇文章开始时提出的问题,我需要单独为换行符定义一个判断函数:


static gboolean is_line_break(const gchar *s) {
  return (!strcmp(s, "n") ? TRUE : FALSE);
}

解决问题