我无法理解之间的差异std::string
和std::wstring
。我知道wstring
支持宽字符,例如Unicode字符。我有以下问题:
- 我什么时候应该
std::wstring
用完std::string
? - 可以
std::string
容纳整个ASCII字符集,包括特殊字符吗? - 是
std::wstring
由所有流行的C ++编译器的支持? - 什么是“ 宽字符 ”?
我无法理解之间的差异std::string
和std::wstring
。我知道wstring
支持宽字符,例如Unicode字符。我有以下问题:
std::wstring
用完std::string
?std::string
容纳整个ASCII字符集,包括特殊字符吗?std::wstring
由所有流行的C ++编译器的支持?Answers:
string
?wstring
?std::string
是basic_string
在char
和std::wstring
上的模板wchar_t
。
char
与 wchar_t
char
应该包含一个字符,通常是8位字符。
wchar_t
应该具有宽字符,然后事情变得棘手:
在Linux上,a wchar_t
为4字节,而在Windows上为2字节。
问题是既没有char
也不wchar_t
直接与unicode绑定。
让我们以Linux操作系统为例:我的Ubuntu系统已经支持Unicode。当我使用char字符串时,它是本地编码为UTF-8(即Unicode的char字符串)。如下代码:
#include <cstring>
#include <iostream>
int main(int argc, char* argv[])
{
const char text[] = "olé" ;
std::cout << "sizeof(char) : " << sizeof(char) << std::endl ;
std::cout << "text : " << text << std::endl ;
std::cout << "sizeof(text) : " << sizeof(text) << std::endl ;
std::cout << "strlen(text) : " << strlen(text) << std::endl ;
std::cout << "text(ordinals) :" ;
for(size_t i = 0, iMax = strlen(text); i < iMax; ++i)
{
std::cout << " " << static_cast<unsigned int>(
static_cast<unsigned char>(text[i])
);
}
std::cout << std::endl << std::endl ;
// - - -
const wchar_t wtext[] = L"olé" ;
std::cout << "sizeof(wchar_t) : " << sizeof(wchar_t) << std::endl ;
//std::cout << "wtext : " << wtext << std::endl ; <- error
std::cout << "wtext : UNABLE TO CONVERT NATIVELY." << std::endl ;
std::wcout << L"wtext : " << wtext << std::endl;
std::cout << "sizeof(wtext) : " << sizeof(wtext) << std::endl ;
std::cout << "wcslen(wtext) : " << wcslen(wtext) << std::endl ;
std::cout << "wtext(ordinals) :" ;
for(size_t i = 0, iMax = wcslen(wtext); i < iMax; ++i)
{
std::cout << " " << static_cast<unsigned int>(
static_cast<unsigned short>(wtext[i])
);
}
std::cout << std::endl << std::endl ;
return 0;
}
输出以下文本:
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
您会看到“olé”文本char
实际上是由四个字符构成的:110、108、195 和169(不计算结尾的零)。(我将让您学习wchar_t
代码作为练习)
因此,char
在Linux上使用时,通常应该在不知道的情况下最终使用Unicode。并且与std::string
一起使用时char
,std::string
已经可以使用unicode了。
请注意std::string
,与C字符串API一样,它将认为“olé”字符串具有4个字符,而不是3个字符。因此,在截断/播放unicode字符时,请务必谨慎,因为UTF-8中禁止使用某些字符组合。
在Windows上,这有点不同。在Unicode出现之前,Win32必须支持许多应用程序,这些应用程序可以char
与世界各地生产的不同字符集 / 代码页一起使用,并可以在这些字符集 / 代码页上使用。
因此,他们的解决方案是一个有趣的解决方案:如果应用程序使用char
,则使用机器上的本地字符集/代码页将字符字符串编码/打印/显示在GUI标签上。例如,在法语本地化的Windows中,“olé”将是“olé”,但是在西里尔语本地化的Windows中,“olé”将有所不同(如果使用Windows-1251,则为“olй” )。因此,“历史应用程序”通常仍将以相同的旧方式工作。
对于基于Unicode的应用程序,Windows使用wchar_t
2字节宽的,并以UTF-16编码,而UTF-16是2字节字符的Unicode编码(或者至少是最兼容的UCS-2,几乎是UCS-2 IIRC)。
使用char
的应用程序称为“多字节”(因为每个字形由一个或多个char
s组成),而使用wchar_t
的应用程序称为“ widechar”(因为每个字形由一个或两个组成wchar_t
。有关更多信息,请参见MultiByteToWideChar和WideCharToMultiByte Win32转换API。
因此,如果您在Windows上工作,则极需要使用wchar_t
(除非您使用隐藏了该框架的框架,例如GTK +或QT ...)。事实是,在幕后,Windows使用wchar_t
字符串,因此即使使用API (在Win32 GUI上设置标签的低级API函数),即使是历史应用程序也将char
转换其字符串。wchar_t
SetWindowText()
UTF-32是每个字符4个字节,因此,如果仅UTF-8文本和UTF-16文本将始终比UTF-32文本使用更少或相同的内存量(通常更少),则无需添加太多内容)。
如果存在内存问题,那么您应该比大多数西方语言都知道,与相同的UTF-16相比,UTF-8文本将使用更少的内存。
但是,对于其他语言(中文,日文等),与UTF-16相比,UTF-8使用的内存将相同或稍大。
总而言之,UTF-16每个字符最多使用2个字节,有时使用4个字节(除非您要处理某种深奥的语言字形(Klingon?Elvish?),而UTF-8则使用1到4个字节。
有关更多信息,请参见http://en.wikipedia.org/wiki/UTF-8#Compared_to_UTF-16。
什么时候应该在std :: string上使用std :: wstring?
在Linux上?几乎从不 (§)。
在Windows上?几乎总是 (§)。
在跨平台代码上?取决于您的工具箱...
(§):除非您使用工具箱/框架,否则
可以std::string
容纳所有ASCII字符集,包括特殊字符吗?
注意:A std::string
适合于容纳“二进制”缓冲区,而a std::wstring
不适合!
在Linux上?是。
在Windows上?仅特殊字符可用于Windows用户的当前区域设置。
编辑(在Johann Gerell发表评论之后):
a std::string
足以处理所有char
基于字符串的字符串(每个字符串char
都是0到255之间的数字)。但:
char
s不是ASCII。char
从0到127将被正确举行char
128到255之间的符号将取决于您的编码(unicode,non-unicode等),但是只要它们以UTF-8编码,它就可以保存所有Unicode字形。是std::wstring
几乎所有流行的C ++编译器的支持?
通常,除了已移植到Windows的基于GCC的编译器。
它适用于我的g ++ 4.3.2(在Linux下),并且自Visual C ++ 6起,我就在Win32上使用了Unicode API。
什么是宽字?
在C / C ++上,它是一种wchar_t
比简单char
字符类型大的字符类型。它应该用于放入索引大于255(或127,取决于...)的字符(如Unicode字形)。
olè
编码为UTF-8的实现定义的行为。更进一步,您无法本地传输wchar_t *
到的原因std::cout
是因为类型不兼容,导致程序格式错误,并且与编码的使用无关。值得指出的是,是使用std::string
还是std::wstring
取决于自己的编码首选项而不是平台,特别是如果您希望代码可移植。
std::wstring
应该在Windows上使用的结论,因为它更适合Unicode Windows API,我认为这是错误的。如果您唯一关心的是调用Unicode Windows API而不是编组字符串,那么可以肯定,但是我不一般购买。
If your only concern was calling into the Unicode Windows API and not marshalling strings then sure
那,我们同意。我使用C ++而不是JavaScript进行编码。该语言的核心是避免在运行时进行无用的编组或任何其他潜在的昂贵处理,这是该语言的核心。针对WinAPI进行编码和使用std::string
只是浪费了运行时资源,这是不合理的。您会发现它很谬误,没关系,因为这是您的观点。我自己的观点是,我不会因为在Linux方面看起来更好而在Windows上使用悲观主义编写代码。
我建议避免std::wstring
在Windows或其他任何地方使用,除非界面需要,或者在Windows API调用和相应的编码转换附近作为语法糖的地方。
我的共同作者http://utf8everywhere.org中概述了我的观点。
除非您的应用程序以API调用为中心,例如主要是UI应用程序,否则建议将Unicode字符串存储在std :: string中并以UTF-8编码,在API调用附近执行转换。本文概述的好处远远超过了转换带来的烦恼,尤其是在复杂的应用程序中。对于多平台和库开发而言,这是双重的。
现在,回答您的问题:
因此,现在这里的每个读者都应该对事实,情况有清楚的了解。如果不是,那么您必须阅读paercebal出色的综合答案 [btw:谢谢!]。
我的务实结论非常简单:所有C ++(和STL)“字符编码”的内容都被破坏了并且毫无用处。无论是否责怪微软,这都无济于事。
经过深入调查后,我的解决方案是:
接受,您必须自己对编码和转换负责(并且您会发现其中的许多内容都很琐碎)
对任何UTF-8编码的字符串使用std :: string(只是一个typedef std::string UTF8String
)
接受这样的UTF8String对象只是一个愚蠢但便宜的容器。永远不要直接访问和/或操纵其中的字符(不要搜索,替换等)。您可以,但是您确实真的真的不想浪费您的时间来编写多字节字符串的文本操作算法!即使其他人已经做过如此愚蠢的事情,也不要这样做!随它去!(嗯,在某些情况下这很有意义……只需使用ICU库即可)。
对UCS-2编码的字符串(typedef std::wstring UCS2String
)使用std :: wstring- 这是一种妥协,是对WIN32 API引入的混乱的让步)。对于我们大多数人来说,UCS-2就足够了(稍后再介绍...)。
每当需要逐个字符访问(读取,操作等)时,请使用UCS2String实例。任何基于字符的处理都应以NON-multibyte表示形式进行。它简单,快速,容易。
添加两个实用程序函数以在UTF-8和UCS-2之间来回转换:
UCS2String ConvertToUCS2( const UTF8String &str );
UTF8String ConvertToUTF8( const UCS2String &str );
转换非常简单,Google应该在这里提供帮助...
而已。在内存很宝贵的地方和所有UTF-8 I / O都使用UTF8String。在必须解析和/或操作字符串的任何地方,请使用UCS2String。您可以随时在这两种表示形式之间进行转换。
替代方案和改进
从&到单字节字符编码(例如ISO-8859-1)的转换可以借助简单的转换表来实现,例如,const wchar_t tt_iso88951[256] = {0,1,2,...};
以及从UCS2到&的适当代码的转换。
如果UCS-2不足,则切换到UCS-4(typedef std::basic_string<uint32_t> UCS2String
)
ICU或其他unicode库?
当您想在字符串中存储宽字符时。wide
取决于实现。如果我没记错的话,Visual C ++默认为16位,而GCC默认取决于目标。这里是32位长。请注意,wchar_t(宽字符类型)与Unicode无关。仅保证它可以存储实现由其语言环境支持的最大字符集的所有成员,至少与char一样长。您也可以使用编码存储 unicode字符串。但是它不理解unicode代码点的含义。所以std::string
utf-8
str.size()
不会为您提供字符串中逻辑字符的数量,而只会为您存储在该字符串/ wstring中的char或wchar_t元素的数量。因此,gtk / glib C ++包装人员开发了Glib::ustring
可以处理utf-8的类。
如果 wchar_t为32位长,则可以utf-32
用作unicode编码,并且可以使用固定(utf-32为固定长度)编码存储和处理unicode字符串。这意味着你的wstring的s.size()
函数,然后返回wchar_t的元素适量和逻辑字符。
std::wstring
。
我经常使用std :: string来保存utf-8字符而没有任何问题。我衷心建议在与使用utf-8作为本机字符串类型的API接口时执行此操作。
例如,在将代码与Tcl解释器接口时,我使用utf-8。
主要警告是std :: string的长度,不再是字符串中的字符数。
对仅256个不同字符不满意的应用程序可以选择使用宽字符(超过8位)或可变长度编码(在C ++术语中为多字节编码),例如UTF-8。宽字符通常比可变长度编码需要更多空间,但处理速度更快。处理大量文本的多语言应用程序在处理文本时通常使用宽字符,但在将其存储到磁盘时将其转换为UTF-8。
a string
和a 之间的唯一区别wstring
是它们存储的字符的数据类型。字符串存储char
的大小保证至少为8位,因此您可以使用字符串进行处理,例如ASCII,ISO-8859-15或UTF-8文本。该标准没有提及字符集或编码。
实际上,每个编译器都使用一个字符集,其前128个字符与ASCII对应。使用UTF-8编码的编译器也是如此。在UTF-8或其他可变长度编码中使用字符串时,要意识到的重要一点是,索引和长度以字节而不是字符为单位。
wstring的数据类型为wchar_t
,其大小在标准中未定义,只是它的大小必须至少与char一样大,通常为16位或32位。wstring可用于在实现定义的宽字符编码中处理文本。由于标准中未定义编码,因此在字符串和wstring之间进行转换并不容易。也不能假设wstring具有固定长度的编码。
如果不需要多语言支持,则只使用常规字符串可能会很好。另一方面,如果要编写图形应用程序,则通常情况下,API仅支持宽字符。然后,您可能希望在处理文本时使用相同的宽字符。请记住,UTF-16是可变长度编码,这意味着您不能假定length()
返回字符数。如果API使用固定长度的编码(例如UCS-2),则处理变得容易。很难以可移植的方式在宽字符和UTF-8之间进行转换,但是同样,您的用户界面API可能也支持转换。
一个好问题!我认为数据编码(有时也涉及CHARSET)是一种内存表达机制,目的是将数据保存到文件或通过网络传输数据,因此我以以下方式回答此问题:
1.什么时候应该在std :: string上使用std :: wstring?
如果编程平台或API函数是单字节的,并且我们要处理或解析某些Unicode数据(例如,从Windows'.REG文件或网络2字节流中读取),则应声明std :: wstring变量以便于处理它们。例如:wstring ws = L“中国a”(6个八位位组内存:0x4E2D 0x56FD 0x0061),我们可以使用ws [0]获取字符“中”,ws [1]获取字符“国”和ws [2]得到字符'a',等等。
2. std :: string是否可以容纳整个ASCII字符集,包括特殊字符?
是。但请注意:美国ASCII,表示每个0x00〜0xFF八位字节代表一个字符,包括可打印文本,例如“ 123abc&* _&”,您说的是特殊字符,通常将其打印为'。'。避免混淆编辑器或终端。还有一些其他国家/地区扩展了自己的“ ASCII”字符集,例如中文,使用2个八位位组代表一个字符。
3,所有流行的C ++编译器都支持std :: wstring吗?
也许,或者大部分。我曾经使用过:VC ++ 6和GCC 3.3,是的
4.什么是“宽字符”?
宽字符主要表示使用2个八位字节或4个八位字节来保存所有国家/地区的字符。2个八位位组UCS2是一个代表性示例,例如英语'a',其内存为0x0061的2个八位位组(相对于ASCII'a's的内存为1个八位位组0x61)
这里有一些很好的答案,但是我认为我可以添加一些有关Windows / Visual Studio的东西。这是基于我对VS2015的经验。在Linux上,基本上,答案是std::string
到处都使用UTF-8编码。在Windows / VS上,它变得更加复杂。这就是为什么。Windows期望使用char
s 存储的字符串将使用区域设置代码页进行编码。这几乎总是ASCII字符集,然后是128个其他特殊字符,具体取决于您的位置。让我只说一下,不仅在使用Windows API时,这些字符串还在其他三个主要位置与标准C ++进行交互。这些是字符串文字,输出为std::cout
使用<<
并将文件名传递给std::fstream
。
在这里,我将是一名程序员,而不是语言专家。我赞赏USC2和UTF-16并不相同,但是出于我的目的,它们足够接近且可以互换,因此我在此使用它们。我实际上不确定要使用哪个Windows,但我通常也不需要知道。我已经在此答案中说明了UCS2,如果对我无知的事情感到不满意,请提前抱歉,如果遇到问题,我很乐意更改它。
如果输入的字符串文字仅包含可以由代码页表示的字符,则VS会根据代码页以每个字符编码1个字节的形式将它们存储在文件中。请注意,如果您更改代码页或使用其他代码页将源代码提供给另一位开发人员,则我认为(但尚未测试)该字符最终会有所不同。如果您在使用其他代码页的计算机上运行代码,那么我不确定字符是否也会更改。
如果输入代码页无法表示的任何字符串文字,则VS会要求您将文件另存为Unicode。然后,该文件将被编码为UTF-8。这意味着所有非ASCII字符(包括代码页上的字符)将由2个或更多字节表示。这意味着,如果您将源代码提供给其他人,则源代码看起来将相同。但是,在将源传递给编译器之前,VS将UTF-8编码的文本转换为代码页编码的文本,并将代码页中缺少的任何字符替换为?
。
确保在VS中正确表示Unicode字符串文字的唯一方法是在字符串文字之前加上L
一个宽字符串文字。在这种情况下,VS会将文件中的UTF-8编码文本转换为UCS2。然后,您需要将此字符串文字传递给std::wstring
构造函数,或者需要将其转换为utf-8并将其放入std::string
。或者,如果您愿意,可以使用Windows API函数使用代码页将其放在中进行编码std::string
,但是您也可能没有使用宽字符串文字。
使用输出到控制台时,<<
您只能使用std::string
,不能使用,std::wstring
并且必须使用区域设置代码页对文本进行编码。如果有,std::wstring
则必须使用Windows API函数之一对其进行转换,并且代码页上未包含的任何字符都将被替换?
(也许您可以更改字符,我不记得了)。
Windows操作系统使用UCS2 / UTF-16作为其文件名,因此无论您的代码页如何,您都可以使用任何Unicode字符的文件。但这意味着访问或创建代码页中未包含字符的文件时,必须使用std::wstring
。没有别的办法了。这是Microsoft的特定扩展,std::fstream
因此可能无法在其他系统上编译。如果使用std :: string,则只能使用在代码页上仅包含字符的文件名。
如果您只是在Linux上工作,那么您可能还没走得那么远。std::string
随处使用UTF-8 。
如果您仅在Windows上工作,则可在std::wstring
任何地方使用UCS2 。一些纯粹主义者可能会说使用UTF8然后在需要时进行转换,但是为什么要麻烦呢。
如果您是跨平台的,那么坦白地说,这是一团糟。如果您尝试在Windows上的任何地方使用UTF-8,则需要特别注意字符串文字并将其输出到控制台。您可以在此处轻松破坏字符串。如果您std::wstring
在Linux上到处使用,则可能无法访问的宽版本std::fstream
,因此您必须进行转换,但是没有损坏的风险。所以我个人认为这是一个更好的选择。许多人会不同意,但我并不孤单-例如,这就是wxWidgets采取的道路。
另一种选择可能是typedef定义unicodestring
为std::string
在Linux和std::wstring
Windows上,并有一个叫做UNI()在Linux宏观其在Windows前缀L和什么都没有,然后代码
#include <fstream>
#include <string>
#include <iostream>
#include <Windows.h>
#ifdef _WIN32
typedef std::wstring unicodestring;
#define UNI(text) L ## text
std::string formatForConsole(const unicodestring &str)
{
std::string result;
//Call WideCharToMultiByte to do the conversion
return result;
}
#else
typedef std::string unicodestring;
#define UNI(text) text
std::string formatForConsole(const unicodestring &str)
{
return str;
}
#endif
int main()
{
unicodestring fileName(UNI("fileName"));
std::ofstream fout;
fout.open(fileName);
std::cout << formatForConsole(fileName) << std::endl;
return 0;
}
我认为在任何一个平台上都可以。
所以要回答你的问题
1)如果您正在为Windows编程,那么始终都是跨平台的,除非您想在Windows上处理可能的损坏问题或使用平台专门编写一些代码#ifdefs
来解决这些差异,否则就一直使用,除非使用Linux则永远不会。
2)是的。此外,在Linux上,您也可以将其用于所有Unicode。在Windows上,如果您选择使用UTF-8手动编码,则只能将其用于所有unicode。但是Windows API和标准C ++类将期望std::string
使用区域设置代码页对进行编码。其中包括所有ASCII加上另外128个字符,这些字符会根据您的计算机设置为使用的代码页而变化。
3)我相信,但是如果不是,那只是'std :: basic_string'的简单typedef使用,wchar_t
而不是char
4)宽字符是大于1字节标准char
类型的字符类型。在Windows上是2个字节,在Linux上是4个字节。
/utf-8
)时,我认为这不是真的。
1)正如Greg所说,wstring对于国际化很有帮助,那时您将以英语以外的其他语言发布产品
4)检查一下是否包含宽字符 http://en.wikipedia.org/wiki/Wide_character
什么时候不应该使用宽字符?
当您在1990年之前编写代码时。
显然,我正在翻转,但实际上,现在是21世纪。127个字符早就不再足够了。是的,您可以使用UTF8,但是为什么要烦恼头痛呢?
wchar_t
在于它的大小和含义是特定于OS的。它只是将旧问题换成新问题。而a char
是独立char
于操作系统的(至少在类似平台上)。因此,我们不妨使用UTF-8,将所有内容打包到char
s 序列中,并感叹C ++如何完全依靠我们自己,而无需在这些序列中进行任何测量,索引,查找等标准方法。
wchar_t
是固定宽度的数据类型,因此10个数组wchar_t
将始终占据sizeof(wchar_t) * 10
平台字节。UTF-16是一种可变宽度编码,其中的字符可以由1个或2个16位代码点组成(对于UTF-8,则为s / 16/8 / g)。