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

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

现在万事俱备,只欠东风,我们应该着手解决问题了。如果读到此处已经忘记了问题是什么,那么请回顾第一节。

尽管下面这段代码看上去挺丑,但是它能够解决问题。


gboolean is_right_at_sign = TRUE;
glong offset = g_utf8_strlen(demo_text, -1);
gchar *viewer = g_utf8_offset_to_pointer(demo_text, offset - 1);
while (viewer != demo_text) {
  viewer = g_utf8_prev_char(viewer);
  gchar *utf8_char = get_utf8_char(viewer);
  if (!is_space(utf8_char)) {
    if (!is_line_break(utf8_char)) {
      is_right_at_sign = FALSE;
      g_free(utf8_char);
      break;
    } else {
      g_free(utf8_char);
      break;
    }
  }
  g_free(utf8_char);
}
if (is_right_at_sign) g_print("Right @ !n");

对上述代码略做简化,可得:


gboolean is_right_at_sign = TRUE;
glong offset = g_utf8_strlen(demo_text, -1);
gchar *viewer = g_utf8_offset_to_pointer(demo_text, offset - 1);
while (viewer != demo_text) {
  viewer = g_utf8_prev_char(viewer);
  gchar *utf8_char = get_utf8_char(viewer);
  if (!is_space(utf8_char)) {
    if (!is_line_break(utf8_char)) is_right_at_sign = FALSE;
    g_free(utf8_char);
    break;
  }
  g_free(utf8_char);
}
if (is_right_at_sign) g_print("Right @ !n");

其实,如果将 UTF-8 字符的提取与内存释放过程置入 is_space 与 is_line_break 函数,即:


static gboolean is_space(const gchar *c) {
  gboolean ret = FALSE;
  gchar *utf8_char = get_utf8_char(c);
  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(utf8_char, space_chars_set[i])) {
      ret = TRUE;
      break;
    }
  }
  g_free(utf8_char);
  return ret;
}

static gboolean is_line_break(const gchar *c) {
  gboolean ret = FALSE;
  gchar *utf8_char = get_utf8_char(c);
  if (!strcmp(utf8_char, "n")) ret = TRUE;
  g_free(utf8_char);
  return ret;
}

可以得到进一步的简化结果:


gboolean is_right_at_sign = TRUE;
glong offset = g_utf8_strlen(demo_text, -1);
gchar *viewer = g_utf8_offset_to_pointer(demo_text, offset - 1);
while (viewer != demo_text) {
  viewer = g_utf8_prev_char(viewer);
  if (!is_space(viewer)) {
    if (!is_line_break(viewer)) is_right_at_sign = FALSE;
    break;
  }
}
if (is_right_at_sign) g_print("Right @ !n");

附:完整的代码


#include <string.h>
#include <glib.h>

gchar *demo_text =
  "我的 C81 每天都在口袋里n"
  "      @";

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;
}

static gboolean is_space(const gchar *c) {
  gboolean ret = FALSE;
  gchar *utf8_char = get_utf8_char(c);
  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(utf8_char, space_chars_set[i])) {
      ret = TRUE;
      break;
    }
  }
  g_free(utf8_char);
  return ret;
}

static gboolean is_line_break(const gchar *c) {
  gboolean ret = FALSE;
  gchar *utf8_char = get_utf8_char(c);
  if (!strcmp(utf8_char, "n")) ret = TRUE;
  g_free(utf8_char);
  return ret;
}

int main(void) {
  gboolean is_right_at_sign = TRUE;
  glong offset = g_utf8_strlen(demo_text, -1);
  gchar *viewer = g_utf8_offset_to_pointer(demo_text, offset - 1);
  while (viewer != demo_text) {
    viewer = g_utf8_prev_char(viewer);
    if (!is_space(viewer)) {
      if (!is_line_break(viewer)) is_right_at_sign = FALSE;
      break;
    }
  }
  if (is_right_at_sign) g_print("Right @ !n");

  return 0;
}