标准在[basic.fundamental]/5中说:
类型是一个独立的类型,其值可以表示所有支持语言环境中最大的扩展字符集的所有成员的不同代码。类型应具有与其他整数类型之一相同的大小、符号和对齐要求,称为其基础类型。类型和表示具有与中的和相同大小、符号和对齐要求的不同类型,称为其基础类型。
所以,如果我想处理Unicode字符,我应该使用吗? 等价地,我如何知道特定的Unicode字符是否被支持?
wchar_t
吗?wchar_t
一样使用char
来表示Unicode字符 - 您只需要记住,根据UTF-8、UTF-16或UTF-32编码,最多4个char
一起将形成一个有效代码点,而wchar_t
可以使用1个(在Linux等上使用UTF-32)或最多2个一起工作(在Windows上使用UTF-16)。一旦您做出决定,您应该最小化转换量并保持决策的一致性。
在下一步中,您可以决定适合表示数据的数据类型(或需要进行哪种类型的转换)。
如果您想要基于代码点进行文本操作/解释,则 char
肯定不是一个好选择,特别是当您拥有例如日语汉字时。但是,如果您只是想要传达数据并且将其视为不再是字节的数量序列,那么您可以选择使用 char
。
UTF-8 everywhere 的链接已经作为评论发布了,我建议您也去看一下。另一篇好文章是What every programmer should know about encodings 。
到目前为止,在C++中仅支持Unicode的基本语言支持(例如 char16_t
和 char32_t
数据类型以及 u8
/u
/U
字面前缀)。因此,选择用于管理编码(特别是转换)的库肯定是一个好建议。
UTF8-CPP-> uint32_t utf8::next(...);
这样的库来拆分std::string
,那么每个结果中的每个“项”都需要占用32位吗?这种浪费在内存中是不可避免的吗?或者只有使用可变长度的标准才有意义,当谈论磁盘存储时才会出现? - Saddle Pointwchar_t
在Windows中使用UTF16-LE格式。 wchar_t
需要宽字符函数,例如wcslen(const wchar_t*)
而不是strlen(const char*)
和std::wstring
而不是std::string
基于Unix的机器(Linux,Mac等)使用UTF8。这使用char
进行存储,并且对于ASCII使用相同的C和C++函数,例如strlen(const char*)
和std::string
(有关std::find_first_of
的注释见下文)
wchar_t
在Windows中是2字节(UTF16)。但在其他机器上是4字节(UTF32)。这使事情更加混乱。
对于UTF32,可以使用std::u32string
在不同系统上都一样。
您可能考虑将UTF8转换为UTF32,因为这样每个字符始终为4个字节,并且您可能认为字符串操作会更容易。 但那很少必要。
UTF8被设计为使0到128之间的ASCII字符不用于表示其他Unicode代码点。 这包括转义序列'\'
,printf
格式说明符以及常用解析字符,例如,
考虑以下UTF8字符串。假设您想要找到逗号
std::string str = u8"汉,"; //3 code points represented by 8 bytes
逗号的ASCII值为44
,并且str
保证只包含一个字节,其值为44
。要查找逗号,可以使用C或C++中的任何标准函数来查找','
。
要查找汉
,可以搜索字符串u8"汉"
,因为此代码点无法表示为单个字符。
一些C和C++函数在处理UTF8时不太流畅。这些函数包括
strtok
strspn
std::find_first_of
以上函数的参数是一组字符,而不是实际的字符串。
因此,str.find_first_of(u8"汉")
不能工作。因为u8"汉"
占用3个字节,而find_first_of
将查找这些字节中的任意一个。有可能其中一个字节被用来表示另一个码点。
另一方面,str.find_first_of(u8",;abcd")
是安全的,因为搜索参数中的所有字符都是ASCII字符(str
本身可以包含任何Unicode字符)
在罕见的情况下可能需要使用UTF32(尽管我无法想象在哪里需要!)。 您可以使用std::codecvt
将UTF8转换为UTF32以运行以下操作:
std::u32string u32 = U"012汉"; //4 code points, represented by 4 elements
cout << u32.find_first_of(U"汉") << endl; //outputs 3
cout << u32.find_first_of(U'汉') << endl; //outputs 3
顺便提一下:
您应该“到处使用Unicode”,而不是“到处使用UTF8”。
在Linux、Mac等系统中使用UTF8表示Unicode。
在Windows系统中,使用UTF16表示Unicode。Windows程序员使用UTF16,他们不会将其转换为UTF8然后再转回来,这样做没有意义。但是,在Windows中使用UTF8也是有合理情况的。
Windows程序员倾向于使用UTF8保存文件、网页等内容。这对于非Windows程序员来说在兼容性方面就不用太担心了。
编程语言本身并不关心您想使用哪种Unicode格式,但从实用角度考虑,应该选择与您所在的操作系统匹配的格式。
std :: basic_string
并没有提供任何实际功能。icu :: UnicodeString
与 std :: basic_string
没有任何关系。 该怎么办? 在整个代码中专门使用 icu :: UnicodeString
? 可能不是。std :: basic_string
派生的自定义字符串类,类似于此:typedef wchar_t mychar_t; // say
class MyString : public std::basic_string <mychar_t>
{
...
};
你可以灵活选择在容器中存储的代码单元的大小。但你可以做更多的事情。例如,使用以上声明(并在其中添加必要的构造函数的样板),你仍然无法这样说:
MyString s = "abcde";
std::basic_string <wchar_t>
的构造函数都期望宽字符串。微软通过宏(TEXT("...")
或__T("...")
)解决了这个问题,但这很麻烦。现在我们只需要在MyString
中提供一个适当的构造函数,签名为MyString(const char *s)
,问题就解决了。MyString
使用的基础字符宽度是什么,并在必要时进行转换。有人在这里评论说,你应该将字符串存储为UTF-8,这样你就可以从代码中的UTF-8文字面量构造它们。好吧,现在我们已经打破了这个限制。我们字符串的基础字符宽度可以是任何我们喜欢的东西。find_first_of
对于UTF-8字符串(以及一些UTF-16字符串)可能无法正常工作。现在,您可以提供一个正确执行此操作的实现。应该只需要大约半小时的时间。如果std::basic_string
中还有其他“损坏”的实现(我确信有),则大多数实现可能都可以被类似的方式替换。icu::UnicodeString
。这可能是大多数人会做的事情。const WCHAR *
(再次,您会以这样的方式实现它们,以使它们适用于mychar_t的所有值)。或者您可以进一步抽象平台和库所提供的Unicode支持的某些或全部内容。例如,Mac具有丰富的Unicode支持,但它仅从Objective-C中可用,因此您必须进行包装。这取决于您希望代码的可移植性程度。std::basic_string
的能力。或多或少。只是尽量不要编写假定自己知道宽度或不包含代理对的代码。
u8char_t
之前请使用char
来处理UTF-8,并使用一个好的Unicode库,如ICU(记住:到处都要用UTF-8)。 - hltwchar_t
足够的区域设置而正式符合语言标准。然后 作为扩展,您可以使用更多带有代理对的区域设置,但这不受 C++ 标准支持。 - Bo Persson