【问题标题】:iterate over characters blocks in utf-8迭代 utf-8 中的字符块
【发布时间】:2013-06-12 14:33:39
【问题描述】:

我的任务是遍历与给定语言(区域设置)对应的所有 utf-8 字符代码。我想这并不容易,我必须遍历字符块(例如,“ru_RU”的整个西里尔字母)。我可以在 wiki 页面https://en.wikipedia.org/wiki/UTF-8 上找到字符块,但我希望有比发明自己的自行车更好的方法。

我看过 icu-project,但我不知道我是否可以做我需要的。

我想要的结果是这样的:

for (unsignet int=UBLOCK_GREEK_EXTENDED; i<UBLOCK_GREEK_EXTENDED_SIZE; i++) {
    // do stuff
}

icu-project 是一个非常强大的工具,所以我希望有人知道如何做到这一点:)

更新: 我正在为移动设备的 3D 框架开发本地化选项。它对真字体进行光栅化和编码,因此可以通过从光栅化字体文件中选择所需的图像来轻松渲染它们。由于我必须关心内存量,我想为不同的语言环境(或语言,或像 cirylic 或希腊语的字符块)在不同的文件中拆分光栅化字体,所以我不必将整个 utf-8 字体保留在内存中一直,但只在检测到语言环境后才加载相应的文件。

谢谢!

【问题讨论】:

  • 您为什么要这样做,也就是您要解决什么问题? meta.stackexchange.com/questions/66377/what-is-the-xy-problem/…
  • 我已在帖子中添加了更新。谢谢你的回答。
  • 那么你想要一个给定语言使用哪些字符的列表?
  • 这将是最好的选择。 Characters = utf-8 中的字符代码,但我想我可以使用 icu 获取代码。
  • 为什么你(单数,作为这个函数的实现者)关心你的调用者使用的是哪个语言环境?您是否尝试在迭代的同时执行验证?您是否认为您需要语言环境信息来判断字符从其高位开始的字节数。

标签: c++ utf-8 locale icu


【解决方案1】:

虽然 UTF 映射中有专门针对某些语言的部分,但您的意思并不完全清楚 - 例如。正如您对希腊语所说的那样-有很多语言的字符被分成许多不同的区域-例如许多欧洲语言使用 ASCII 字母 - A-Z 等 - 并且还从 160-240 区域中设置的“扩展拉丁语 1”中选择字符。

所以任何“迭代”的工具都说罗马尼亚语必须首先确定罗马尼亚语是哪些字符,然后用 UTF 识别它们,然后打印它们。

如果您根本不是这个意思,而是想从 UTF 打印出特定的分组,我建议您考虑使用 UTF32 作为基本编码,这样打印字符会容易得多。

【讨论】:

    【解决方案2】:

    语言块在 unicode are listed here 中的列表,因此您可以将字符的大部分字符拆分到自己的文件中。

    您需要列出每个呈现的字体文件中可用的字符,然后为呈现的每个字符串中的字符加载适当的字体文件。

    但是 - 动态地执行此操作可能不是一个好主意,因为它可能很慢(检查每个字符)并且当字符滑入不在任何字符集中的字符时容易失败。

    你最好反过来做;当有人初始化您的引擎时,他们会列出您应该加载哪些语言块,并加载相应的文件。然后,当您渲染字符串时,只需删除当前不可用的任何字符。

    【讨论】:

    • 这正是我想做的。问题是我是否可以使用诸如 icu 之类的库来获取所请求语言的字符代码。我可以使用您提到的文档并定义超过 9000 个常量...但我更喜欢使用库 :) 如何加载字体并检测应该在运行时使用哪个字体不是问题。
    • "我可以使用文档...并定义超过 9000 个常量" Erm,将它们定义为范围怎么样?西里尔字母 = U+0400 -> U+04FF
    • 我明白你的意思,我同意。也许你是对的,但不是那么多。
    • 是的 - 总共只列出了 220 个块,其中大部分与您无关,例如多米诺块fileformat.info/info/unicode/block/domino_tiles/images.htm
    • 现在我更好地理解了这个问题,我建议不要使用这种方法。例如,虽然您可以说“我的设备是英国的,所以我不需要西里尔字母”,但今天有很多情况需要奇怪的字符,除非您完全控制要打印的文本。例如,考虑一个法国国民,他们的名字需要重音字母,但用英语生活和使用它?我建议,最好实现一个体面的字体缓存,以仅读取使用的字符。务必使用预期语言环境的字符预先填充缓存...
    【解决方案3】:

    所以,我终于资助了正确使用 icu-project 库 http://site.icu-project.org 的方法。

    这是一个示例解决方案。您指定区域设置或语言,并获取包含与区域设置/语言相关的符号的 utf-8 字符块数组。然后,您可以获取每个字符块的开始和结束。

    UErrorCode err = U_ZERO_ERROR;
    const int32_t capacity = 10;
    const char* shortname = NULL;
    int32_t num, j;
    int32_t strLength = 4;
    UScriptCode script[10] = {USCRIPT_INVALID_CODE};
    num = uscript_getCode("en", script, capacity, &err);
    UnicodeString temp = UnicodeString("[", 1, US_INV);
    UnicodeString pattern;
    for(j=0; j<num; j++) {
        shortname = uscript_getShortName(script[j]);
        UnicodeString str(shortname, strLength, US_INV);
        temp.append("[:");
        temp.append(str);
        temp.append(":]+");
    }
    pattern = temp.remove(temp.length()-1,1);
    pattern.append("]");
    
    UnicodeSet cnvSet(pattern, err);
    printf("Number of script code associated are : %d \n", num);
    printf("Range count: %d\n", cnvSet.getRangeCount());
    printf("Set size: %d\n", cnvSet.size());
    for(int32_t i=0; i<cnvSet.getRangeCount(); i++) {
        printf("Range start: %x\n", cnvSet.getRangeStart(i));
        printf("Range end: %x\n", cnvSet.getRangeEnd(i));
    }
    

    此示例中语言“en”的结果:

    关联的脚本代码数为:1

    范围计数:30

    设置大小:1272

    范围开始:41

    射程结束:5a

    范围开始:61

    射程结束:7a

    ...

    范围开始:ff41

    范围结束:ff5a

    表示与拉丁文块相对应的所有字符范围。

    【讨论】:

      【解决方案4】:

      一种语言中实际使用的字符可以在exemplar sets 中找到,定义在CLDR 中。

      我不会构建一个复杂的 UnicodeSet,而是迭代 u+0000…u+10fff 并测试 uscript_getScript (UChar32 codepoint, UErrorCode *err) 返回的脚本 - UnicodeSet 将在内部对您作为答案提供的示例代码执行相同的操作.

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-08-19
        • 2011-04-09
        • 1970-01-01
        • 2020-09-29
        • 2018-12-02
        • 2021-09-08
        • 1970-01-01
        相关资源
        最近更新 更多