1) 验证进入 wchar_t 的值是否正确。生成宽字符串文字的编译器必须将L"ä"
源代码编码转换为宽执行字符集。
2) 验证程序的语言环境是否正确。你可以这样做printf("%s\n", setlocale(LC_ALL, NULL));
我怀疑问题是 1) 因为对我来说,即使程序的语言环境设置不正确,我仍然可以获得预期的输出。为避免源代码编码出现问题,您可以转义非 ascii 字符,例如L"\x00E4"
.
1 #include <iostream>
2 #include <clocale>
3
4 int main () {
5 std::printf("%s\n", std::setlocale(LC_ALL, NULL)); // prints "C"
6
7 char a[10];
8 wchar_t w[10] = L"\x00E4"; // German a Umlaut
9 std::printf("0x%04x\n", (unsigned)w[0]); // prints "0x00e4"
10
11 std::setlocale(LC_ALL, "");
12 printf("%s\n", std::setlocale(LC_ALL, NULL)); // print something that indicates the encoding is ISO 8859-1
13 int e = std::wcstombs(a, w, 10);
14 std::printf("%i 0x%02x\n", e, (unsigned char)a[0]); // print "1 0xe4"
15 }
16
C 和 C++ 程序中的字符集
在您的源代码中,您可以使用“源字符集”中的任何字符,它是“基本源字符集”的超集。编译器会将字符串和字符文字中的字符从源字符集中转换为执行字符集(或宽字符串和字符文字的宽执行字符集)。
问题是源字符集依赖于实现。通常,编译器只需要知道您对源代码使用什么编码,然后它将接受来自该编码的任何字符。GCC 具有用于设置源编码的命令行参数,Visual Studio 将假定源在用户的代码页中,除非它检测到 UTF-8 或 UTF-16 的所谓 Unicode 签名之一,并且 Clang 当前始终使用 UTF- 8.
一旦编译器为您的代码使用正确的源字符集,它将在“执行字符集”中生成字符串和字符文字。执行字符集是基本源字符集的另一个超集,也依赖于实现。GCC 采用命令行参数来设置执行字符集,VS 使用用户的语言环境,Clang 使用 UTF-8。
因为源字符集依赖于实现,所以在基本集之外写入字符的可移植方式是使用十六进制编码直接指定要在执行中使用的数值,或者(如果您不使用 C89/90)使用通用字符名称 (UCN),将其转换为执行字符集(或用于宽字符串和字符文字时的宽执行字符集)。UCN 看起来像 \uNNNN 或 \UNNNNNNNN,并使用代码点值 NNNN 或 NNNNNNNN 指定 Unicode 字符集中的字符。(请注意,C99 和 C++11 禁止您使用代理代码点,如果您想要来自 BMP 外部的字符,只需使用 \U 直接写入该字符的值。)
源和执行字符集是在编译时确定的,不会根据运行程序的系统的语言环境而改变。也就是说,程序语言环境使用了另一种不一定与执行字符集匹配的编码。但是,宽执行字符集应与支持的语言环境使用的宽字符编码相对应。
Solaris Studio 的行为
Oracle 的 Solaris 编译器具有非常简单的行为。对于窄字符串和字符文字,没有指定特定的源编码,源代码中的字节直接用作执行文字。这实际上意味着执行字符集与源文件的编码相同。对于宽字符文字,源字节使用系统语言环境进行转换。这意味着您必须使用语言环境编码保存源文件才能获得正确的宽文字。
我怀疑您的源代码以不同于语言环境指定的编码保存,因此您的编译器无法从L"ä"
. 您的编辑器可能正在使用 UTF-8。您可以使用以下程序进行检查。
1 #include <iostream>
2 #include <clocale>
3
4 int main () {
5 wchar_t w[10] = L"ä"; // German a Umlaut
6 std::printf("0x%04x 0x%04x\n", (unsigned)w[0], (unsigned)w[1]);
7 }
8
由于 wcstombs 可以正确地将宽字符 0x00E4 转换为 'ä' 的 latin-1 编码,因此您希望上面显示0x00E4 0x0000
. 如果源代码编码是 UTF-8,那么您应该看到0x00C3 0x00A4
.