我无法理解std::string和std::wstring之间的区别。我知道wstring支持Unicode字符等宽字符。我有以下问题:
什么时候应该使用std::wstring而不是std::string?std::string可以保存整个ASCII字符集,包括特殊字符吗?所有流行的C++编译器都支持std::wstring吗?什么是“宽字符”?
我无法理解std::string和std::wstring之间的区别。我知道wstring支持Unicode字符等宽字符。我有以下问题:
什么时候应该使用std::wstring而不是std::string?std::string可以保存整个ASCII字符集,包括特殊字符吗?所有流行的C++编译器都支持std::wstring吗?什么是“宽字符”?
当前回答
一串wstring?
std::string是在char上模板化的basicstring,而std::wstring是在wchart上模板化。
字符与wchar_t
char应该包含一个字符,通常是8位字符。wchar_t应该包含一个宽字符,然后,事情变得棘手:在Linux上,wchar_t是4字节,而在Windows上,它是2字节。
那么Unicode呢?
问题是char和wchar_t都没有直接绑定到unicode。
在Linux上?
让我们以Linux操作系统为例:我的Ubuntu系统已经支持unicode。当我使用字符串时,它是以UTF-8(即Unicode字符串)本机编码的。以下代码:
#include <cstring>
#include <iostream>
int main()
{
const char text[] = "olé";
std::cout << "sizeof(char) : " << sizeof(char) << "\n";
std::cout << "text : " << text << "\n";
std::cout << "sizeof(text) : " << sizeof(text) << "\n";
std::cout << "strlen(text) : " << strlen(text) << "\n";
std::cout << "text(ordinals) :";
for(size_t i = 0, iMax = strlen(text); i < iMax; ++i)
{
unsigned char c = static_cast<unsigned_char>(text[i]);
std::cout << " " << static_cast<unsigned int>(c);
}
std::cout << "\n\n";
// - - -
const wchar_t wtext[] = L"olé" ;
std::cout << "sizeof(wchar_t) : " << sizeof(wchar_t) << "\n";
//std::cout << "wtext : " << wtext << "\n"; <- error
std::cout << "wtext : UNABLE TO CONVERT NATIVELY." << "\n";
std::wcout << L"wtext : " << wtext << "\n";
std::cout << "sizeof(wtext) : " << sizeof(wtext) << "\n";
std::cout << "wcslen(wtext) : " << wcslen(wtext) << "\n";
std::cout << "wtext(ordinals) :";
for(size_t i = 0, iMax = wcslen(wtext); i < iMax; ++i)
{
unsigned short wc = static_cast<unsigned short>(wtext[i]);
std::cout << " " << static_cast<unsigned int>(wc);
}
std::cout << "\n\n";
}
输出以下文本:
sizeof(char) : 1
text : olé
sizeof(text) : 5
strlen(text) : 4
text(ordinals) : 111 108 195 169
sizeof(wchar_t) : 4
wtext : UNABLE TO CONVERT NATIVELY.
wtext : ol�
sizeof(wtext) : 16
wcslen(wtext) : 3
wtext(ordinals) : 111 108 233
您将看到char中的“olé”文本实际上由四个字符构成:110、108、195和169(不包括后面的零)。(我将让您学习wchar_t代码作为练习)
因此,在Linux上使用字符时,您通常会在不知道的情况下使用Unicode。由于std::string可以使用字符,所以std::字符串已经可以使用Unicode。
请注意,std::string与C字符串API一样,会认为“olé”字符串有4个字符,而不是3个字符。因此,在截断/播放unicode字符时应谨慎,因为UTF-8中禁止某些字符组合。
在Windows上?
在Windows上,这有点不同。在Unicode出现之前,Win32必须支持许多使用字符和世界各地产生的不同字符集/代码页的应用程序。
因此,他们的解决方案是一个有趣的解决方案:如果应用程序使用字符,那么字符字符串将使用机器上的本地字符集/代码页编码/打印/显示在GUI标签上,这在很长一段时间内都不可能是UTF-8。例如,在法语本地化的Windows中,“olé”将是“olé”,但在cyrillic本地化的Windows上则会有所不同(如果使用Windows-1251,则为“olй”)。因此,“历史应用程序”通常仍将以旧的方式工作。
对于基于Unicode的应用程序,Windows使用wchar_t,其宽度为2字节,并以UTF-16编码,UTF-16以2字节字符为Unicode编码(或者至少是UCS-2,它只是缺少代理对,因此缺少BMP之外的字符(>=64K))。
使用字符的应用程序称为“多字节”(因为每个字形由一个或多个字符组成),而使用wchar_t的应用程序则称为“宽字符”(因为每一个字形由一或两个wchar_t)。有关详细信息,请参阅MultiByteToWideChar和WideCharToMultiByteWin32转换API。
因此,如果你在Windows上工作,你很想使用wchar_t(除非你使用一个隐藏它的框架,如GTK或QT…)。事实是,在幕后,Windows使用wchar_t字符串,所以即使是历史应用程序,在使用SetWindowText()(用于在Win32 GUI上设置标签的低级API函数)等API时,也会将其字符字符串转换为wchar_t。
内存问题?
UTF-32是每个字符4个字节,因此没有什么可添加的,只要UTF-8文本和UTF-16文本总是比UTF-32文本使用更少或相同的内存量(通常更少)。
如果存在内存问题,那么您应该知道,与大多数西方语言相比,UTF-8文本使用的内存将少于相同的UTF-16文本。
尽管如此,对于其他语言(中文、日语等),UTF-8使用的内存将与UTF-16相同,或者略大。
总而言之,UTF-16通常每个字符使用2个字节,有时使用4个字节(除非您正在处理某种深奥的语言字形(克林贡语?精灵语?),而UTF-8将使用1到4个字节。
看见https://en.wikipedia.org/wiki/UTF-8#Compared_to_UTF-16获取更多信息。
结论
什么时候应该在std::string上使用std::wstring?在Linux上?几乎从未(§)。在Windows上?几乎总是(§)。跨平台代码?取决于您的工具包。。。(§):除非您使用的工具包/框架另有说明std::string可以保存所有ASCII字符集,包括特殊字符吗?注意:std::string适合保存“binary”缓冲区,而std::wstring不是!在Linux上?对在Windows上?只有特殊字符可用于Windows用户的当前区域设置。编辑(Johann Gerell发表评论后):一个std::字符串将足以处理所有基于字符的字符串(每个字符都是从0到255的数字)。但是:ASCII应该从0到127。较高的字符不是ASCII码。从0到127的字符将被正确保存从128到255的字符将根据您的编码(unicode、非unicode等)而有意义,但只要以UTF-8编码,它将能够保存所有unicode字形。几乎所有流行的C++编译器都支持std::wstring吗?大多数情况下,除了移植到Windows的基于GCC的编译器。它适用于我的g++4.3.2(在Linux下),我从Visual C++6开始在Win32上使用Unicode API。宽字符到底是什么?在C/C++上,它是一种wchar_t编写的字符类型,比简单的char字符类型更大。它应该用于放置索引(如Unicode字形)大于255(或127,取决于…)的字符。
其他回答
什么时候不应该使用宽字符?
当你在1990年之前编写代码时。
很明显,我很生气,但事实上,现在是21世纪。127个字符早已不再足够。是的,你可以使用UTF8,但为什么要头疼呢?
一串wstring?
std::string是在char上模板化的basicstring,而std::wstring是在wchart上模板化。
字符与wchar_t
char应该包含一个字符,通常是8位字符。wchar_t应该包含一个宽字符,然后,事情变得棘手:在Linux上,wchar_t是4字节,而在Windows上,它是2字节。
那么Unicode呢?
问题是char和wchar_t都没有直接绑定到unicode。
在Linux上?
让我们以Linux操作系统为例:我的Ubuntu系统已经支持unicode。当我使用字符串时,它是以UTF-8(即Unicode字符串)本机编码的。以下代码:
#include <cstring>
#include <iostream>
int main()
{
const char text[] = "olé";
std::cout << "sizeof(char) : " << sizeof(char) << "\n";
std::cout << "text : " << text << "\n";
std::cout << "sizeof(text) : " << sizeof(text) << "\n";
std::cout << "strlen(text) : " << strlen(text) << "\n";
std::cout << "text(ordinals) :";
for(size_t i = 0, iMax = strlen(text); i < iMax; ++i)
{
unsigned char c = static_cast<unsigned_char>(text[i]);
std::cout << " " << static_cast<unsigned int>(c);
}
std::cout << "\n\n";
// - - -
const wchar_t wtext[] = L"olé" ;
std::cout << "sizeof(wchar_t) : " << sizeof(wchar_t) << "\n";
//std::cout << "wtext : " << wtext << "\n"; <- error
std::cout << "wtext : UNABLE TO CONVERT NATIVELY." << "\n";
std::wcout << L"wtext : " << wtext << "\n";
std::cout << "sizeof(wtext) : " << sizeof(wtext) << "\n";
std::cout << "wcslen(wtext) : " << wcslen(wtext) << "\n";
std::cout << "wtext(ordinals) :";
for(size_t i = 0, iMax = wcslen(wtext); i < iMax; ++i)
{
unsigned short wc = static_cast<unsigned short>(wtext[i]);
std::cout << " " << static_cast<unsigned int>(wc);
}
std::cout << "\n\n";
}
输出以下文本:
sizeof(char) : 1
text : olé
sizeof(text) : 5
strlen(text) : 4
text(ordinals) : 111 108 195 169
sizeof(wchar_t) : 4
wtext : UNABLE TO CONVERT NATIVELY.
wtext : ol�
sizeof(wtext) : 16
wcslen(wtext) : 3
wtext(ordinals) : 111 108 233
您将看到char中的“olé”文本实际上由四个字符构成:110、108、195和169(不包括后面的零)。(我将让您学习wchar_t代码作为练习)
因此,在Linux上使用字符时,您通常会在不知道的情况下使用Unicode。由于std::string可以使用字符,所以std::字符串已经可以使用Unicode。
请注意,std::string与C字符串API一样,会认为“olé”字符串有4个字符,而不是3个字符。因此,在截断/播放unicode字符时应谨慎,因为UTF-8中禁止某些字符组合。
在Windows上?
在Windows上,这有点不同。在Unicode出现之前,Win32必须支持许多使用字符和世界各地产生的不同字符集/代码页的应用程序。
因此,他们的解决方案是一个有趣的解决方案:如果应用程序使用字符,那么字符字符串将使用机器上的本地字符集/代码页编码/打印/显示在GUI标签上,这在很长一段时间内都不可能是UTF-8。例如,在法语本地化的Windows中,“olé”将是“olé”,但在cyrillic本地化的Windows上则会有所不同(如果使用Windows-1251,则为“olй”)。因此,“历史应用程序”通常仍将以旧的方式工作。
对于基于Unicode的应用程序,Windows使用wchar_t,其宽度为2字节,并以UTF-16编码,UTF-16以2字节字符为Unicode编码(或者至少是UCS-2,它只是缺少代理对,因此缺少BMP之外的字符(>=64K))。
使用字符的应用程序称为“多字节”(因为每个字形由一个或多个字符组成),而使用wchar_t的应用程序则称为“宽字符”(因为每一个字形由一或两个wchar_t)。有关详细信息,请参阅MultiByteToWideChar和WideCharToMultiByteWin32转换API。
因此,如果你在Windows上工作,你很想使用wchar_t(除非你使用一个隐藏它的框架,如GTK或QT…)。事实是,在幕后,Windows使用wchar_t字符串,所以即使是历史应用程序,在使用SetWindowText()(用于在Win32 GUI上设置标签的低级API函数)等API时,也会将其字符字符串转换为wchar_t。
内存问题?
UTF-32是每个字符4个字节,因此没有什么可添加的,只要UTF-8文本和UTF-16文本总是比UTF-32文本使用更少或相同的内存量(通常更少)。
如果存在内存问题,那么您应该知道,与大多数西方语言相比,UTF-8文本使用的内存将少于相同的UTF-16文本。
尽管如此,对于其他语言(中文、日语等),UTF-8使用的内存将与UTF-16相同,或者略大。
总而言之,UTF-16通常每个字符使用2个字节,有时使用4个字节(除非您正在处理某种深奥的语言字形(克林贡语?精灵语?),而UTF-8将使用1到4个字节。
看见https://en.wikipedia.org/wiki/UTF-8#Compared_to_UTF-16获取更多信息。
结论
什么时候应该在std::string上使用std::wstring?在Linux上?几乎从未(§)。在Windows上?几乎总是(§)。跨平台代码?取决于您的工具包。。。(§):除非您使用的工具包/框架另有说明std::string可以保存所有ASCII字符集,包括特殊字符吗?注意:std::string适合保存“binary”缓冲区,而std::wstring不是!在Linux上?对在Windows上?只有特殊字符可用于Windows用户的当前区域设置。编辑(Johann Gerell发表评论后):一个std::字符串将足以处理所有基于字符的字符串(每个字符都是从0到255的数字)。但是:ASCII应该从0到127。较高的字符不是ASCII码。从0到127的字符将被正确保存从128到255的字符将根据您的编码(unicode、非unicode等)而有意义,但只要以UTF-8编码,它将能够保存所有unicode字形。几乎所有流行的C++编译器都支持std::wstring吗?大多数情况下,除了移植到Windows的基于GCC的编译器。它适用于我的g++4.3.2(在Linux下),我从Visual C++6开始在Win32上使用Unicode API。宽字符到底是什么?在C/C++上,它是一种wchar_t编写的字符类型,比简单的char字符类型更大。它应该用于放置索引(如Unicode字形)大于255(或127,取决于…)的字符。
当您希望在字符串中存储宽字符时。宽取决于实现。如果我没记错的话,Visual C++默认为16位,而GCC默认值取决于目标。这里有32位长。请注意wchar_t(宽字符类型)与unicode无关。它只是保证它可以存储实现所支持的最大字符集的所有成员,并且至少可以存储char。也可以使用utf-8编码将unicode字符串精细地存储到std::string中。但它无法理解unicode代码点的含义。因此str.size()不会给出字符串中逻辑字符的数量,而只给出该字符串/wstring中存储的char或wchar_t元素的数量。出于这个原因,gtk/glib C++包装人员开发了一个可以处理utf-8的glib::ustring类。如果wchar_t是32位长,那么可以使用utf-32作为unicode编码,并且可以使用固定(utf-32是固定长度)编码来存储和处理unicode字符串。这意味着wstring的s.size()函数将返回正确数量的wchar_t元素和逻辑字符。是的,char总是至少8位长,这意味着它可以存储所有ASCII值。是的,所有主要的编译器都支持它。
我经常使用std::string来保存utf-8字符,而没有任何问题。我强烈建议在与使用utf-8作为本机字符串类型的API接口时这样做。
例如,当我的代码与Tcl解释器接口时,我使用utf-8。
主要的警告是std::字符串的长度,不再是字符串中的字符数。
1) 正如Greg所提到的,wstring有助于国际化,这意味着您将以英语以外的语言发布产品
4) 检查此项以获取宽字符http://en.wikipedia.org/wiki/Wide_character