根据cppreference.com's doc on wchar_t
:
wchar_t
- 用于宽字符表示的类型(请参阅宽字符串)。要求足够大以表示任何支持的字符代码点(在支持Unicode的系统上为32位。一个值得注意的例外是Windows,其中wchar_t是16位并且保存UTF-16代码单元)它具有相同的大小,签名和对齐作为整数类型之一,但是是一种不同的类型。
标准在[basic.fundamental]/5
中说:
类型
wchar_t
是一种不同的类型,其值可以表示支持的语言环境中指定的最大扩展字符集的所有成员的不同代码。类型wchar_t
应具有与其他整数类型相同的大小,符号和对齐要求,称为其基础类型。类型char16_t
和char32_t
分别在uint_least16_t
中表示与uint_least32_t
和<cstdint>
相同的大小,签名和对齐方式的不同类型,称为基础类型
所以,如果我想处理unicode字符,我应该使用wchar_t
吗?
等效地,我如何知道wchar_t
是否支持某个特定的unicode字符??
答案 0 :(得分:13)
所以,如果我想处理unicode字符,我应该使用
wchar_t
吗
首先,请注意编码不会强制您使用任何特定类型来表示某个字符。您可以使用char
来表示Unicode字符,就像wchar_t
一样 - 您只需要记住,最多4个char
将形成一个有效的代码点,具体取决于UTF-8,UTF -16或UTF-32编码,而wchar_t
可以使用1(Linux上的UTF-32等)或最多2个一起工作(Windows上的UTF-16)。
接下来,没有明确的Unicode编码。一些Unicode编码使用固定宽度表示代码点(如UTF-32),其他(如UTF-8和UTF-16)具有可变长度(例如,字母'a'肯定只使用1个字节,但是分开从英文字母表中,其他字符肯定会用更多的字节来表示)。
因此,您必须决定要表示的字符类型,然后相应地选择您的编码。根据您要表示的字符类型,这将影响数据将占用的字节数。例如。使用UTF-32来表示大多数英文字符将导致许多0字节。 UTF-8是许多拉丁语言的更好选择,而UTF-16通常是东亚语言的更好选择。
一旦您做出决定,您应该尽量减少转化次数,并与您的决定保持一致。
在下一步中,您可以决定哪种数据类型适合表示数据(或者您可能需要哪种转换)。
如果您希望在代码点的基础上进行文本操作/解释,char
当然,如果您有例如char
日本汉字。但是,如果您只想传达数据并将其视为字节的定量序列,则可以使用char16_t
。
UTF-8 everywhere的链接已作为评论发布,我建议您也查看一下。另一个好的读物是What every programmer should know about encodings。
到目前为止,C ++中只有基本语言支持Unicode(如char32_t
和u8
数据类型,u
/ U
/ {{1文字前缀)。因此选择一个用于管理编码(尤其是转换)的库肯定是一个很好的建议。
答案 1 :(得分:8)
wchar_t
,它使用UTF16-LE格式。 wchar_t
需要广泛的char函数。例如wcslen(const wchar_t*)
代替strlen(const char*)
和std::wstring
代替std::string
char
进行存储,使用相同的C和C ++函数,例如strlen(const char*)
和std::string
(请参阅下面有关 std::find_first_of
的评论)
Windows中的 wchar_t
是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
旁注:
你应该在任何地方使用&#34; Unicode&#34; ,而不是&#34;到处都是UTF8&#34; 。
在Linux,Mac等中使用UTF8进行Unicode。
在Windows中,将UTF16用于Unicode。 Windows程序员使用UTF16,他们不会在UTF8之间来回进行无意义的转换。但是在Windows中使用UTF8有合法的案例。
Windows程序员倾向于使用UTF8来保存文件,网页等。因此,在兼容性方面,非Windows程序员不必担心。
语言本身并不关心您要使用哪种Unicode格式,但在实用性方面,请使用与您正在使用的系统相匹配的格式。
答案 2 :(得分:5)
所以,如果我想处理unicode字符,我应该使用wchar_t吗?
这取决于您正在处理的编码。在UTF-8的情况下,你可以使用char和std :: string。 UTF- 8 表示最小编码单位是8位:从U + 0000到U + 007F的所有Unicode代码点仅由1个字节编码。 从代码点U + 0080开始,UTF-8使用2个字节进行编码,从U + 0800开始,它使用3个字节,从U + 10000个4个字节开始。 要处理这个可变宽度(1字节 - 2字节 - 3字节 - 4字节),char最适合。 请注意,像strlen这样的C函数将提供基于字节的结果:“öö”实际上是一个双字符文本,但strlen将返回4,因为'ö'被编码为0xC3B6。
UTF- 16 表示最小编码单位是16位:从U + 0000到U + FFFF的所有代码点都由2个字节编码;从U + 100000开始使用4个字节。 如果是UTF-16,你应该使用wchar_t和std :: wstring,因为你遇到的大多数字符都是2字节编码的。 使用wchar_t时,你不能再使用像strlen这样的C函数了;你必须使用像wcslen这样的宽字符。
使用Visual Studio并使用配置“Unicode”构建时,您将获得UTF-16:TCHAR和CString将基于wchar_t而不是char。
答案 3 :(得分:4)
这完全取决于你所说的'处理'的含义,但有一点是肯定的:在涉及Unicode的地方std::basic_string
根本不提供任何真正的功能。
在任何特定程序中,您将需要执行X个可识别Unicode的操作,例如:智能字符串匹配,大小写折叠,正则表达式,定位分词,使用Unicode字符串作为路径名,等等。
支持这些操作几乎总会有平台提供的某种库和/或本机API,我的目标是以这样的方式存储和操作我的字符串,使得这些操作可以在没有在整个代码中分散底层库和本机API支持的知识,超出必要的范围。我还希望自己能够在将来保存自己在字符串中保存的字符的宽度,以防我改变主意。
例如,假设您决定使用ICU进行繁重的工作。立即出现了一个明显的问题:icu::UnicodeString
与std::basic_string
没有任何关系。该怎么办?在整个代码中专门使用icu::UnicodeString
?可能不是。
或者应用程序的重点可能从欧洲语言转换为亚洲语言,因此UTF-16成为(也许)比UTF-8更好的选择。
所以,我的选择是使用从std::basic_string
派生的自定义字符串类,如下所示:
typedef wchar_t mychar_t; // say
class MyString : public std::basic_string <mychar_t>
{
...
};
直接您可以灵活选择容器中存储的代码单元的大小。但你可以做的远不止这些。例如,使用上面的声明(并且在为需要提供的各种构造函数添加样板文件以将它们转发到std::basic_string
之后),您仍然不能说:
MyString s = "abcde";
因为“abcde”是一个很窄的字符串,std::basic_string <wchar_t>
的各种构造函数都需要一个宽字符串。微软用宏(TEXT ("...")
或__T ("...")
)来解决这个问题,但这很痛苦。我们现在需要做的就是在MyString
中提供一个合适的构造函数,签名为MyString (const char *s)
,问题就解决了。
实际上,无论用于MyString
的基础字符宽度如何,此构造函数都可能需要UTF-8字符串,并在必要时进行转换。有人在这里评论你应该将你的字符串存储为UTF-8,以便你可以在你的代码中用UTF-8文字构造它们。那么现在我们已经打破了这个限制。字符串的基础字符宽度可以是我们喜欢的任何东西。
人们在这个帖子中谈到的另一件事是find_first_of
可能无法正常用于UTF-8字符串(实际上也有一些UTF-16字符串)。那么,现在您可以提供正确完成工作的实现。应该需要大约半个小时。如果std::basic_string
中有其他“破坏”的实现(我确定有),那么大多数实现可能会被替换为类似的简单。
至于其余部分,它主要取决于你想在MyString类中实现什么级别的抽象。例如,如果您的应用程序很乐意依赖ICU,那么您可以提供几种方法来转换为icu::UnicodeString
。这可能是大多数人会做的事情。
或者,如果您需要将UTF-16字符串传递给本地Windows API或从本地Windows API传递,那么您可以添加与const WCHAR *
进行转换的方法(您将再次实现它们适用于所有值的方式) of mychar_t)。或者您可以进一步抽象出您正在使用的平台和库提供的部分或全部Unicode支持。例如,Mac具有丰富的Unicode支持,但它只能从Objective-C获得,因此您必须将其包装起来。
这取决于您希望代码的可移植性。
因此,您可以添加任何您喜欢的功能,可能会随着工作的进展而不断增加,而不会失去以std::basic_string
为单位的能力。某种或那种。只是尽量不要编写代码,假设它知道它有多宽,或者它不包含surrogate pairs。
答案 4 :(得分:3)
首先,如果你使用的是wchar_t
为16位的Windows和Visual Studio C ++,你应该检查(正如你在问题中指出的那样),因为在这种情况下,要使用完整的unicode支持,你需要&# 39; ll需要采用UTF-16编码。
这里的基本问题不是您正在使用的sizeof wchar_t
,但如果您要使用的库,则支持完整的unicode支持。
Java有一个类似的问题,因为它的char
类型是16位宽,所以它不能先验支持完整的unicode空间,但它确实> strong>,因为它使用UTF-16编码和对代理来处理完整的24位代码点。
值得注意的是,UNICODE仅使用高平面来编码罕见的代码点,这些代码点通常不会每天使用。
无论如何,对于unicode支持,您需要使用宽字符集,因此wchar_t
是一个良好的开端。如果您打算使用visual studio,那么您必须检查它的库如何处理unicode字符。
另一件需要注意的事情是标准库只在你添加语言环境支持时处理字符集(这包括unicode)(这需要初始化一些库,例如setlocale(3)
),所以,你&#39;在没有调用setlocale(3)
的情况下,我们根本看不到unicode(只有基本的ascii)。
几乎所有str*(3)
函数以及任何stdio.h
库函数都有广泛的char函数来处理wchar_t
。稍微深入/usr/include/wchar.h
文件将显示例程的名称。请访问手册页以获取有关它们的文档:fgetws(3)
,fputwc(3)
,fputws(3)
,fwide(3)
,fwprintf(3)
,...
最后,再次考虑一下,如果您正在处理Microsoft Visual C ++,那么从一开始就有不同的实现。即使他们应该完全符合标准,您也必须应对具有不同实现的一些特性。可能你会为某些用途使用不同的函数名称。