在 C++ 中查找和比较 Unicode 字符

Posted

技术标签:

【中文标题】在 C++ 中查找和比较 Unicode 字符【英文标题】:Finding and comparing a Unicode charater in C++ 【发布时间】:2020-10-10 20:48:59 【问题描述】:

我正在编写一个词法分析器,用于解析 C++ 中的给定字符串。我有一个字符串

line = R"(if n = 4 # comment
             return 34;  
             if n≤3 retur N1
          FI)";

我需要做的就是在一个向量中输出所有单词、数字和标记。

我的程序使用常规标记、单词和数字;但我不知道如何解析 Unicode 字符。我的程序需要保存在向量中的唯一 Unicode 字符是 ≤ 和 ≠。

到目前为止,我所有的代码基本上都是逐行读取字符串,读取第一个单词、数字或标记,将其切掉并递归地继续吃标记,直到字符串为空。我无法将line[0] 进行比较(当然),而且我也不清楚为了摆脱Unicode 字符需要剪掉多少字符串?如果是"!=",我只需删除line[0]line[1]

【问题讨论】:

您的字符串是否编码为 UTF-8?如果是这样,请参阅 this post 以了解如何通过名为 widen 的函数将它们转换为 std::wstring。这些将更容易处理。 @PaulSanders。我不同意,请参阅utf8everywhere.org。 utf8 可能有问题的唯一架构是在 MS-Windows 下。但是 Windows 使用 utf16 表示 std::wstring,这是两全其美的。你仍然有多字节的问题,有趣的字节顺序添加等等。 @PaulSanders 转换为 UTF-16 并不能解决问题,因为您仍然需要处理 BMP 之外的字符。 UTF-16 不是固定宽度的编码,你只需要读取 2 个字节 @phuclv 我没有说我正在转换为 UTF-16。引用的代码转换为std::wstring。也就是说,在 UTF-16 中处理 BMP 之外的字符并不困难,因为用于代理对中各个代码单元的值在明确定义的范围内。 @PaulSanders 然后处理 UTF-8 更容易,因为范围也定义明确 【参考方案1】:

如果您的输入文件是 utf8,只需将您的 unicode 字符 等视为字符串。因此,您只需使用与识别"<=" 相同的逻辑来识别"≤"。然后由strlen("≤") 给出一个unicode char 的长度

【讨论】:

你还不知道下一个字符是什么,所以 你怎么知道调用 strlen("≤") 是 ≤? 如果你已经知道了那么 strlen 是不必要的,因为您已经知道长度。要识别“≤”,您需要在阅读和识别它之前知道它的长度 @phuclv,发帖人特别说他知道如何识别"!="并将其从字节流中删除。识别!= 是通过逐字节匹配输入流的开头与以零结尾的字符串"!=" 来完成的。识别 是通过逐字节匹配输入流的开头与以零结尾的字符串"≤""≠" 来完成的。发帖人还暗示他不明白如何从输入流中删除。对于!=,他知道要删除2个字节,对于"≠",他应该删除strlen("≠")字节【参考方案2】:

除 UTF-32 外,所有 Unicode 编码都是可变长度的。因此,下一个字符不必是单个字符,您必须将其读取为 string。由于您使用的是char*std::string,因此编码可能是UTF-8 和下一个字符,可以返回为std::string

UTF-8 的编码非常简单,你可以在任何地方读到它。简而言之,序列的第一个字节将指示该序列的长度,您可以像这样获取下一个字符:

std::string getNextChar(const std::string& str, size_t index)

    if (str[index] & 0x80 == 0)            // 1-byte sequence
        return std::string(1, str[index])
    else if (str[index] & 0xE0 == 0xC0)    // 2-byte sequence
        return std::string(&str[index], 2)
    else if (str[index] & 0xF0 == 0xE0)    // 3-byte sequence
        return std::string(&str[index], 3)
    else if (str[index] & 0xF8 == 0xF0)    // 4-byte sequence
        return std::string(&str[index], 4)
    throw "Invalid codepoint!";

这是一个非常简单的解码器,还不能处理无效的代码点或损坏的数据流。如果您需要更好的处理,则必须使用适当的 UTF-8 库

【讨论】:

这比它必须的要复杂得多。几乎不需要将 utf8 字符串拆分为单独的代码点来进行字符串匹配。只需在 utf8 字符串中查找 unicode 字符,然后在输入流中查找这些字节序列。当逐字节比较工作得很好时,无需逐个代码点进行比较

以上是关于在 C++ 中查找和比较 Unicode 字符的主要内容,如果未能解决你的问题,请参考以下文章

使用 boost 和标准 C++ 的 Unicode 安全查找

什么正则表达式仅在 C++ 源文件中查找字符串

JS - 字符编码 (ASCII,Unicode,UTF-8)

纯 C++ 中的 Unicode 字符串

Unicode与编码方式

C++ 中的 Unicode 字符串处理