标准C++并不适用于Unicode的正确处理,会给你带来像你观察到的那样的问题。
问题在于C++比Unicode早得多。这意味着即使是你的字符串文字也将以一种实现定义的方式解释,因为这些字符未在基本源字符集中定义(基本上是ASCII-7字符减去@
、$
和反引号)。
C++98根本没有提到Unicode。它提到了wchar_t
,以及基于它的wstring
,指定wchar_t
能够“表示当前语言环境中的任何字符”。但这造成了更多的伤害...
Microsoft将wchar_t
定义为16位,这足以表示Unicode代码点当时的范围。然而,自那以后,Unicode已经扩展到16位范围之外......并且Windows的16位wchar_t
不再是“宽”的,因为您需要两个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',而标准函数——一次处理一个字符——无法做到这一点。)
然而,有帮助可寻。国际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()
{
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[]
返回的是代码单元(code unit),而不是代码点(code point),因此您可能会得到代理对的一半。请查阅
API文档,了解其他索引Unicode字符串的方法。
std::wstring
和std::wcout
。 - Cory Kramer