1

我正在 MSVC 中编写 Lexer,我需要一种方法来表示所有128 个基本拉丁 unicode字符的精确字符匹配。 但是,根据这篇 MSDN 文章“除了 0x24 和 0x40,0 到 0x20 和 0x7f 到 0x9f 范围内的字符不能用通用字符名称 (UCN) 表示。” ...这基本上意味着我不能声明类似的东西,更不用说在这个“不允许的”字符范围内使用 switch 语句了。此外,对于 '\n' 和 '\r',据我了解,编译器/目标操作系统之间的实际值/长度会有所不同...... (即 Windows 使用 '\r\n',而 Unix 仅使用 '\ n'


wchar_t c = '\u0000';

...因此,我使用通用字符对此进行了解决,以确保检测并正确使用正确的编码方案和字节长度。但是这个C3850

编译器错误只是拒绝让我按照自己的方式做事......那么如何以确保正确编码方案和字符匹配
的方式解决这个问题?

4

2 回答 2

3

在 C++11 中,对可以用通用字符名称表示的字符的限制不适用于字符和字符串文字。

C++11 2.3/2

此外,如果字符或字符串文字的c-char-sequences-char-sequencer-char-sequence之外的通用字符名称的十六进制值对应于控制字符(在任一范围内0x00–0x1F 或 0x7F–0x9F,均包括在内)或基本源字符集中的字符,程序格式错误。 15

这意味着对 UCN 的这些限制不适用于字符和字符串文字:

wchar_t c = L'\u0000'; // perfectly okay

switch(c) {
    case L'\u0000':
        ;
}

这在 C++03 中有所不同,我从您的问题中假设 Microsoft 尚未更新其编译器以允许这样做。但是我认为这并不重要,因为使用 UCN 并不能解决您要解决的问题。

因此,我使用通用字符对此进行了解决,以确保检测到并正确使用正确的编码方案和字节长度

使用 UCN 不会确定所使用的编码方案。UCN 是一种在源代码中包含特定字符的独立于源代码编码的方法,但编译器需要将其视为与源代码中的字面意思完全一样。

例如,拿代码:

int main() {
    unsigned char c = 'µ';
    std::cout << (int)c << '\n';
}

如果您将源代码保存为 UTF-16 并在配置为使用代码页 1252 的 Windows 系统上使用 Microsoft 的编译器构建它,那么编译器会将 'µ' 的 UTF-16 表示转换为 CP1252 表示。如果您在配置有不同代码页(不包含字符)的系统上构建此源代码,则编译器将在无法将字符转换为该代码页时给出警告/错误。

同样,如果您将源代码保存为 UTF-8(使用所谓的 'BOM',以便编译器知道编码是 UTF-8),那么它会将字符的 UTF-8 源表示形式转换为系统的如果可能的话,代码页,不管是什么。

如果你用 UCN 替换 'µ','\u00B5',编译器仍然会做同样的事情;如果可能,它将 UCN 转换为 U+00B5 MICRO SIGN 的系统代码页表示。


那么如何以确保在给定任何源输入的情况下正确的编码方案和字符匹配的方式来解决这个问题呢?

我不确定你在问什么。我猜你想确保整数值charwchar_t变量/文字与某种编码方案一致(可能是 ASCII,因为你只询问 ASCII 范围内的字符),但什么是“源输入”?词法分析器源文件的编码?词法分析器的输入编码?您希望“源输入”如何变化?


此外,对于 '\n' 和 '\r',据我了解,编译器/目标操作系统之间的实际值/长度会有所不同......(即 Windows 使用 '\r\n',而 Unix 仅使用 '\ n' 和旧版本的 MacOS 使用 '\r')

这是对文本模式 I/O 的误解。当您将字符 '\n' 写入文本模式文件时,操作系统可以将 '\n' 字符替换为新行的某些平台特定表示。但是,这并不意味着 '\n' 的实际值有任何不同。更改纯粹在用于写入文件的库中进行。

例如你可以以文本模式打开一个文件,写入'\n',然后以二进制模式打开文件并将写入的数据与'\n'进行比较,写入的数据可能与'\n'不同:

#include <fstream>
#include <iostream>

int main() {
    char const * filename = "test.txt";
    {
        std::ofstream fout(filename);
        fout << '\n';
    }
    {
        std::ifstream fin(filename, std::ios::binary);
        char buf[100] = {};
        fin.read(buf, sizeof(buf));
        if (sizeof('\n') == fin.gcount() && buf[0] == '\n') {
            std::cout << "text mode written '\\n' matches value of '\\n'\n";
        } else {
            // This will be executed on Windows
            std::cout << "text mode written '\\n' does not match value of '\\n'\n";
        }
    }
}

这也不依赖于使用 '\n' 语法;0xA您可以使用ASCII 换行符重写上面的内容,结果在 Windows 上将是相同的。(即,当您将字节写入0xA文本模式文件时,Windows 实际上会写入这两个字节0xD 0xA。)

于 2013-03-10T17:24:44.977 回答
0

我发现省略字符串文字并简单地使用字符的十六进制值可以让一切编译得很好。

例如,您将更改以下行:

wchar_t c = L'\u0000';

...至:

wchar_t c = 0x0000;

不过,我仍然不确定这是否真的拥有由 UCN 提供的相同的独立值。

于 2013-03-10T18:52:45.540 回答