【问题标题】:Advance a UTF-8 character to the next将 UTF-8 字符推进到下一个字符
【发布时间】:2011-06-21 18:31:00
【问题描述】:

我想更改一个 UTF-8 字符(在 gchar 数组中),因此它根据标准获取下一个字符的值。我正在使用 glib,但没有看到这样的功能。我正在考虑一个可能的解决方案,但这可能需要更多的努力,而且肯定不是最有效的,因为我对编码不太了解。有没有图书馆可以做到这一点?谷歌搜索没有帮助。

【问题讨论】:

  • 您会在代码点 0x7f、0x7ff、0xffff 等处遇到麻烦。下一个增量将需要更多空间。最好创建一个新字符串,而不是尝试就地进行。
  • @Hans:创建一个新字符串 3 次比 N(~100 万?)次要便宜得多。 :-)
  • 我不知道,谢谢你的提示。
  • 我怀疑大多数 glib/gtk 程序员没有意识到这种事情或者懒得去想它,这就是为什么这些程序往往如此缓慢和臃肿......
  • 嘿!我们确实考虑过这种事情!=)。不管怎样,我认为自己是一个新手 glib/gtk 程序员,这是一个肯定永远不会看到光明的小程序,所以它不慢,也不臃肿 =)

标签: c string encoding utf-8 glib


【解决方案1】:

这本质上只是模 64 的加法进位。将字符的字节视为“数字”。递增最后一个字节,如果溢出,将其重置为可能的最小值,然后递增倒数第二个字节。

例如一个简单的增量:

e0 b0 be -> e0 b0 bf

单进位增量:

e0 b0 bf -> e0 b1 80

还有一个带有双进位的增量:

e0 bf bf -> e1 80 80

当您增加超过给定大小的最后一个字符时,您需要转到下一个大小的第一个字符,这当然不能在字符串中间就地完成。

【讨论】:

  • 请注意,无论您在这里使用我的算法还是其他算法,您都需要手动跳过至少不是 Unicode 标量值的非法值(代理 D800-DFFF)。如果您还想跳过非字符,那将是更多的工作。
  • 我会将此标记为答案。这就是我的想法。无论如何,我只会前进到 3 个字节长的字符,然后再到 1 个字节。我会跳过非字符,但那些是普通 ascii 和从 80 到 A0 的那些,不是吗?
  • 非字符不同于非法字节序列。它们包括 Unicode 标量值,例如 U+FFFE、U+FFFF、U+1FFFE 等。U+FDD0..FDEF 也保留为非字符。这些代码点对于不同 UTF 之间的转换是合法的,并供应用程序内部使用,但不能与其他应用程序/外部世界交换。
  • 此外,由于您似乎不知道,除了没有头字节的单独 UTF-8 尾字节之外,还有许多其他非法字节序列。 80-BF 都是尾字节,没有头就无效,而字节 C0-C1F5-FF 在 UTF-8 中在任何地方都非法。此外,某些头字节对可以跟随它们的尾字节有限制。例如,E0 80 80 是非法的,但 EO A0 80 是合法的。请注意,ED 后跟 A0-BF 是非法的,因为这些编码将表示不是 Unicode 标量值的代理项。
【解决方案2】:

如果你想避免直接的字节黑客攻击,你可以这样做(未经测试):

gunichar c;
int len, old_len;
char buf[6];

c = g_utf8_get_char(s);
old_len = g_unichar_to_utf8(c, NULL);
c += 1;
len = g_unichar_to_utf8(c, buf);
if (len == old_len) {
  memcpy(s, buf, len);
} else {
  /* something more complex adjusting s length */
}

当然,手动编写会为您提供更优化的代码。对上面的小优化可能会使用 g_utf8_next_char() 来获取下一个字符串位置,并从中计算 old_len,而不是独立计算 old_len。

【讨论】:

  • 谢谢,这看起来是我没想到的另一个好选择
  • 如果您没有准备好阅读规范并处理为 UTF-8 编写正确代码的问题,那么您最好选择这个答案......即使使用这种方法,您也需要跳过代理项范围至少。
  • 是的,我知道。但我可能会以另一种方式去做,因为我喜欢去低级学习新事物。
猜你喜欢
  • 2015-06-05
  • 2012-11-10
  • 2012-01-20
  • 2023-03-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-05-31
  • 2016-07-01
相关资源
最近更新 更多