标准 C++ 不具备正确处理 Unicode 的能力,会给您带来与您观察到的问题类似的问题。
这里的问题是C++ 早于 Unicode 一个舒适的边缘。这意味着即使您的字符串文字也将以 实现定义 方式解释,因为这些字符未在基本源字符集中定义(基本上,ASCII-7 字符减去 @ 987654325@、$ 和反引号)。
C++98 根本没有提到 Unicode。它提到wchar_t 和wstring 基于它,指定wchar_t 能够“表示当前语言环境中的任何字符”。但这弊大于利...
Microsoft 将wchar_t 定义为 16 位,这对于当时的 Unicode 代码点来说已经足够了。但是,从那时起,Unicode 已扩展到 16 位范围之外......并且 Windows 的 16 位 wchar_t 不再“宽”,因为您需要其中两个来表示 BMP 之外的字符 - 和Microsoft 文档臭名昭著模棱两可,wchar_t 表示 UTF-16(具有代理对的多字节编码)或 UCS-2(宽编码,不支持 BMP 以外的字符)。
一直以来,Linux wchar_t 是 32 位的, 宽度足以支持 UTF-32...
C++11 对该主题进行了重大改进,添加了 char16_t 和 char32_t 包括它们相关的 string 变体以消除歧义,但它仍然没有完全支持 Unicode 操作.
作为一个例子,尝试转换例如将德语“Fuß”转为大写,您会明白我的意思。 (单个字母'ß' 需要扩展为'SS',这是标准函数——一次处理一个字符输入,一个字符输出——无法做到的。)
但是,there is help。 Unicode 国际组件 (ICU) 库 完全具备在 C++ 中处理 Unicode 的能力。至于在源代码中指定特殊字符,您将不得不使用 u8""、u"" 和 U"" 来强制将字符串文字分别解释为 UTF-8、UTF-16 和 UTF-32,使用八进制/ 十六进制转义或依赖您的编译器实现来适当地处理非 ASCII-7 编码。
即使这样你也会得到一个整数值std::cout << ramp[5],因为对于C++,一个字符只是一个具有语义意义的整数。 ICU 的ustream.h 为icu::UnicodeString 类提供operator<< 重载,但ramp[5] 只是一个16 位无符号整数(1),如果他们的unsigned short 突然被解释为字符,人们会斜视你.为此,您需要 C-API u_fputs() / u_printf() / u_fprintf() 函数。
#include <unicode/unistr.h>
#include <unicode/ustream.h>
#include <unicode/ustdio.h>
#include <iostream>
int main()
{
// make sure your source file is UTF-8 encoded...
icu::UnicodeString ramp( icu::UnicodeString::fromUTF8( "ÐðŁłŠšÝýÞþŽž" ) );
std::cout << ramp << "\n";
std::cout << ramp[5] << "\n";
u_printf( "%C\n", ramp[5] );
}
使用g++ -std=c++11 testme.cpp -licuio -licuuc编译。
ÐðŁłŠšÝýÞþŽž
353
š
(1) ICU 内部使用 UTF-16,UnicodeString::operator[] 返回代码 unit,而不是代码 point,因此您最终可能会得到一半一个代理对。查找 API docs 以了解索引 unicode 字符串的各种其他方法。