我无法理解之间的差异std::string
和std::wstring
.我知道wstring
支持Unicode字符等宽字符.我有以下问题:
我什么时候应该std::wstring
用完std::string
?
可以std::string
保存整个ASCII字符集,包括特殊字符吗?
是std::wstring
由所有流行的C++编译器的支持?
什么是" 广角 "?
paercebal.. 969
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(即字符串的字符串).以下代码:
#include#include 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 ( static_cast (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 ( static_cast (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标签上编码/打印/显示字符串.例如,"olé"在法语本地化的Windows中将是"olé",但在西里尔语本地化的Windows上会有所不同(如果使用Windows-1251,则为"ol" ).因此,"历史应用程序"通常仍然以相同的方式工作.
对于基于Unicode的应用程序,Windows使用wchar_t
2字节宽,并以UTF-16编码,UTF-16是以2字节字符进行Unicode编码(或者至少是兼容性最强的UCS-2,几乎是同样的事情IIRC).
使用char
的应用程序称为"多字节"(因为每个字形由一个或多个char
s组成),而使用wchar_t
的应用程序称为"widechar"(因为每个字形由一个或两个组成wchar_t
.有关详细信息,请参阅MultiByteToWideChar和WideCharToMultiByte Win32转换API.
因此,如果你在Windows上工作,你非常想使用wchar_t
(除非你使用隐藏它的框架,如GTK +或QT ......).事实是,在幕后,Windows使用wchar_t
字符串,因此即使是历史应用程序也会char
在wchar_t
使用API时将其字符串转换为SetWindowText()
(低级API函数在Win32 GUI上设置标签).
UTF-32每个字符有4个字节,所以没有太多要添加,只要UTF-8文本和UTF-16文本总是使用比UTF-32文本更少或相同的内存量(通常更少) ).
如果存在内存问题,那么您应该知道,对于大多数西方语言,UTF-8文本将使用比相同UTF-16文本更少的内存.
但是,对于其他语言(中文,日文等),UTF-8使用的内存要么相同,要么略大于UTF-16.
总而言之,UTF-16主要使用每个字符2个和偶尔4个字节(除非你正在处理某种深奥的语言字形(Klingon?Elvish?),而UTF-8将花费1到4个字节.
有关详细信息,请参见http://en.wikipedia.org/wiki/UTF-8#Compared_to_UTF-16.
什么时候我应该使用std :: wstring而不是std :: string?
在Linux上?几乎从不 (§).
在Windows上?几乎总是 (§).
在跨平台代码?取决于你的工具包......
(§):除非您使用工具包/框架另有说法
可以std::string
保存包含特殊字符的所有ASCII字符集吗?
注意:A std::string
适合保存'二进制'缓冲区,其中a std::wstring
不是!
在Linux上?是.
在Windows上?只有特殊字符可用于Windows用户的当前区域设置.
编辑(在Johann Gerell的评论之后):
a std::string
足以处理所有char
基于字符串的字符串(每个字符串char
都是0到255之间的数字).但:
ASCII应该从0到127.更高的char
s不是ASCII.
一个char
从0到127将被正确举行
一个char
从128到255将有一个意义取决于您的编码(unicode的,非Unicode等),但它就能只要持有所有Unicode字形,因为它们是UTF-8编码.
是std::wstring
几乎所有流行的C++编译器的支持?
大多数情况下,除了移植到Windows的基于GCC的编译器.
它适用于我的g ++ 4.3.2(在Linux下),我在Win32上使用Unicode API,因为Visual C++ 6.
什么是广泛的角色?
在C/C++上,它是一个wchar_t
大于简单char
字符类型的字符类型.它应该用于放置其索引(如Unicode字形)大于255(或127,取决于...)的字符.
@ John Leidegren:"如果您唯一关心的是调用Unicode Windows API而不是编组字符串那么肯定`:那么,我们同意.我用C++编写代码,而不是JavaScript代码.在编译时可以在运行时避免无用的编组或任何其他可能代价高昂的处理是该语言的核心.对WinAPI进行编码并使用`std :: string`只是一个不合理的浪费运行时资源.你觉得它很谬误,而且没关系,因为这是你的观点.我自己的意思是我不会在Windows上编写带有悲观化的代码,因为它在Linux方面看起来更好. (15认同)
Windows实际上使用UTF-16并且已经有一段时间了,旧版本的Windows确实使用了UCS-2,但现在情况不再如此.我唯一的问题是结论是`std :: wstring`应该在Windows上使用,因为它更适合Unicode Windows API,我认为这是错误的.如果您唯一关心的是调用Unicode Windows API而不是编组字符串,那么肯定但我不会将其作为一般情况购买. (14认同)
虽然这个例子在Linux和Windows上产生了不同的结果,但C++程序包含了关于`olè`是否编码为UTF-8的实现定义行为.此外,您不能*本地*将`wchar_t*`流式传输到`std :: cout`的原因是因为类型不兼容导致程序格式错误,并且与编码的使用无关.值得指出的是,使用`std :: string`还是`std :: wstring`取决于您自己的编码首选项而不是平台,特别是如果您希望代码是可移植的. (8认同)
@gnud:或许wchar_t应该足以在UTF-16出现之前处理所有UCS-2字符(大多数UTF-16字符)......或者微软确实有其他优先级而不是POSIX,比如可以轻松访问Unicode无需在Win32上修改char的codepaged使用. (4认同)
@Sorin Sbarnea:UTF-8可能需要1-6个字节,但显然标准将其限制为1-4.有关更多信息,请参见http://en.wikipedia.org/wiki/UTF8#Description. (4认同)
@paercebal无论平台支持什么,都是完全随意的,除此之外.如果您在Windows内部将所有字符串存储为UTF-8,则必须将它们转换为ANSI或UTF-16并调用相应的Win32函数,但如果您知道您的UTF-8字符串只是纯ASCII字符串,则不会必须做任何事情.该平台并未规定您如何使用字符串. (4认同)
哼.我不知道windows在这方面没有遵循POSIX规范.POSIX表示,wchar_t必须能够为编译环境支持的语言环境中指定的最大字符集的所有成员表示"不同的宽字符代码". (3认同)
"当在Linux上使用char时,你通常应该在不知道它的情况下使用Unicode.并且因为std :: string适用于char,所以std :: string已经是unicode就绪的." - 这应该伴随着一个大的警告"永远不要截断,限制,接受你的字符串".这可以从整个答案中理解,但应该做得非常清楚. (3认同)
@Wolfgang Plaschg:谢谢你的信息.这并不意外,因为MacOS X的是Unix,所以这很自然,他们去的方式"字符是UTF-8"对Unicode的支持......据我所知,唯一的原因,Windows没有遵循同样的道路是继续支持用于基于Unicode的基于charset的旧应用程序. (2认同)
@paercebal UTF-8不能占用6个字节.正是因为标准将其限制为4个字节.标准定义了东西,因此根据定义,6个字节意味着它不再是UTF-8. (2认同)
@paercebal:只是一个注释:其中一种外来语言是中文顺便说一下.因此,中华人民共和国决定在一段时间之前支持BMP之外的一些代码点. (2认同)
@paercebal我意识到这个注释线程与时间本身一样古老,但是出于性能原因坚持匹配WinAPI字符串格式只是愚蠢的。API调用本身的成本将使转换成本相形见;;UTF-16字符串所需的额外存储的性能成本可能会抵消与转换相关的任何潜在收益;并且如果您与其他API通信,则可能仍然需要进行转换。有关示例,请参见http://utf8everywhere.org/#faq.cvt.perf。 (2认同)
Pavel Radziv.. 64
我建议std::wstring
在Windows或其他地方避免使用,除非接口需要,或者在Windows API调用附近的任何地方以及作为语法糖的相应编码转换.
我的观点总结在http://utf8everywhere.org,其中我是其合着者.
除非您的应用程序是以API调用为中心的,例如主要是UI应用程序,否则建议将Unicode字符串存储在std :: string中并以UTF-8编码,在API调用附近执行转换.本文中概述的好处超过了转换的明显烦恼,特别是在复杂的应用程序中.对于多平台和库开发来说,这是双倍的.
现在,回答你的问题:
一些薄弱的原因.它存在是出于历史原因,其中宽带被认为是支持Unicode的正确方式.它现在用于接口更喜欢UTF-16字符串的API.我只在这些API调用的附近使用它们.
这与std :: string无关.它可以保存您放入的任何编码.唯一的问题是你如何对待其内容.我的建议是UTF-8,因此它可以正确保存所有Unicode字符.这是Linux上的常见做法,但我认为Windows程序也应该这样做.
没有.
宽字符是一个令人困惑的名字.在Unicode的早期,人们相信一个字符可以用两个字节编码,因此得名.今天,它代表"两个字节长的角色的任何部分".UTF-16被视为这种字节对的序列(又名宽字符).UTF-16中的字符需要一对或两对.
Frunsi.. 36
所以,现在每个读者都应该清楚地了解事实和情况.如果没有,那么你必须阅读paercebal非常全面的答案 [顺便说一句:谢谢!].
我的实用结论非常简单:所有C++(和STL)"字符编码"的东西都基本上是破碎和无用的.无论如何归咎于微软,无论如何都无济于事.
我的解决方案,经过深入调查,非常沮丧和相应的经验如下:
接受,你必须自己负责编码和转换的东西(你会发现它的大部分都是微不足道的)
对任何UTF-8编码的字符串使用std :: string(只是一个typedef std::string UTF8String
)
接受这样一个UTF8String对象只是一个愚蠢但便宜的容器.永远不要直接访问和/或操纵其中的字符(不搜索,替换等).你可以,但你真的真的,真的不想浪费你的时间为多字节字符串编写文本操作算法!即使其他人已经做过这样的蠢事,也不要这样做!随它去!(嗯,有些情况下有意义......只需使用ICU库).
对于UCS-2编码的字符串使用std :: wstring(typedef std::wstring UCS2String
) - 这是一种妥协,并且是对WIN32 API引入的混乱的让步.UCS-2足以满足我们大多数人的需求(稍后会详细介绍......).
每当需要逐个字符访问(读取,操作等)时,都使用UCS2String实例.任何基于字符的处理都应以非多字节表示形式完成.它简单,快速,简单.
添加两个实用程序函数来在UTF-8和UCS-2之间来回转换:
UCS2String ConvertToUCS2( const UTF8String &str ); UTF8String ConvertToUTF8( const UCS2String &str );
转换很简单,谷歌应该在这里帮助...
而已.在内存珍贵的地方和所有UTF-8 I/O使用UTF8String.在必须解析和/或操作字符串的任何地方使用UCS2String.您可以随时在这两个表示之间进行转换.
替代方案和改进
转换为单字节字符编码(例如ISO-8859-1)可以借助普通转换表实现,例如const wchar_t tt_iso88951[256] = {0,1,2,...};
,用于转换到UCS2和从UCS2转换的适当代码.
如果UCS-2不够,那么切换到UCS-4(typedef std::basic_string
)
ICU或其他unicode库?
对于高级的东西.
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(即字符串的字符串).以下代码:
#include#include 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 ( static_cast (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 ( static_cast (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标签上编码/打印/显示字符串.例如,"olé"在法语本地化的Windows中将是"olé",但在西里尔语本地化的Windows上会有所不同(如果使用Windows-1251,则为"ol" ).因此,"历史应用程序"通常仍然以相同的方式工作.
对于基于Unicode的应用程序,Windows使用wchar_t
2字节宽,并以UTF-16编码,UTF-16是以2字节字符进行Unicode编码(或者至少是兼容性最强的UCS-2,几乎是同样的事情IIRC).
使用char
的应用程序称为"多字节"(因为每个字形由一个或多个char
s组成),而使用wchar_t
的应用程序称为"widechar"(因为每个字形由一个或两个组成wchar_t
.有关详细信息,请参阅MultiByteToWideChar和WideCharToMultiByte Win32转换API.
因此,如果你在Windows上工作,你非常想使用wchar_t
(除非你使用隐藏它的框架,如GTK +或QT ......).事实是,在幕后,Windows使用wchar_t
字符串,因此即使是历史应用程序也会char
在wchar_t
使用API时将其字符串转换为SetWindowText()
(低级API函数在Win32 GUI上设置标签).
UTF-32每个字符有4个字节,所以没有太多要添加,只要UTF-8文本和UTF-16文本总是使用比UTF-32文本更少或相同的内存量(通常更少) ).
如果存在内存问题,那么您应该知道,对于大多数西方语言,UTF-8文本将使用比相同UTF-16文本更少的内存.
但是,对于其他语言(中文,日文等),UTF-8使用的内存要么相同,要么略大于UTF-16.
总而言之,UTF-16主要使用每个字符2个和偶尔4个字节(除非你正在处理某种深奥的语言字形(Klingon?Elvish?),而UTF-8将花费1到4个字节.
有关详细信息,请参见http://en.wikipedia.org/wiki/UTF-8#Compared_to_UTF-16.
什么时候我应该使用std :: wstring而不是std :: string?
在Linux上?几乎从不 (§).
在Windows上?几乎总是 (§).
在跨平台代码?取决于你的工具包......
(§):除非您使用工具包/框架另有说法
可以std::string
保存包含特殊字符的所有ASCII字符集吗?
注意:A std::string
适合保存'二进制'缓冲区,其中a std::wstring
不是!
在Linux上?是.
在Windows上?只有特殊字符可用于Windows用户的当前区域设置.
编辑(在Johann Gerell的评论之后):
a std::string
足以处理所有char
基于字符串的字符串(每个字符串char
都是0到255之间的数字).但:
ASCII应该从0到127.更高的char
s不是ASCII.
一个char
从0到127将被正确举行
一个char
从128到255将有一个意义取决于您的编码(unicode的,非Unicode等),但它就能只要持有所有Unicode字形,因为它们是UTF-8编码.
是std::wstring
几乎所有流行的C++编译器的支持?
大多数情况下,除了移植到Windows的基于GCC的编译器.
它适用于我的g ++ 4.3.2(在Linux下),我在Win32上使用Unicode API,因为Visual C++ 6.
什么是广泛的角色?
在C/C++上,它是一个wchar_t
大于简单char
字符类型的字符类型.它应该用于放置其索引(如Unicode字形)大于255(或127,取决于...)的字符.
我建议std::wstring
在Windows或其他地方避免使用,除非接口需要,或者在Windows API调用附近的任何地方以及作为语法糖的相应编码转换.
我的观点总结在http://utf8everywhere.org,其中我是其合着者.
除非您的应用程序是以API调用为中心的,例如主要是UI应用程序,否则建议将Unicode字符串存储在std :: string中并以UTF-8编码,在API调用附近执行转换.本文中概述的好处超过了转换的明显烦恼,特别是在复杂的应用程序中.对于多平台和库开发来说,这是双倍的.
现在,回答你的问题:
一些薄弱的原因.它存在是出于历史原因,其中宽带被认为是支持Unicode的正确方式.它现在用于接口更喜欢UTF-16字符串的API.我只在这些API调用的附近使用它们.
这与std :: string无关.它可以保存您放入的任何编码.唯一的问题是你如何对待其内容.我的建议是UTF-8,因此它可以正确保存所有Unicode字符.这是Linux上的常见做法,但我认为Windows程序也应该这样做.
没有.
宽字符是一个令人困惑的名字.在Unicode的早期,人们相信一个字符可以用两个字节编码,因此得名.今天,它代表"两个字节长的角色的任何部分".UTF-16被视为这种字节对的序列(又名宽字符).UTF-16中的字符需要一对或两对.
所以,现在每个读者都应该清楚地了解事实和情况.如果没有,那么你必须阅读paercebal非常全面的答案 [顺便说一句:谢谢!].
我的实用结论非常简单:所有C++(和STL)"字符编码"的东西都基本上是破碎和无用的.无论如何归咎于微软,无论如何都无济于事.
我的解决方案,经过深入调查,非常沮丧和相应的经验如下:
接受,你必须自己负责编码和转换的东西(你会发现它的大部分都是微不足道的)
对任何UTF-8编码的字符串使用std :: string(只是一个typedef std::string UTF8String
)
接受这样一个UTF8String对象只是一个愚蠢但便宜的容器.永远不要直接访问和/或操纵其中的字符(不搜索,替换等).你可以,但你真的真的,真的不想浪费你的时间为多字节字符串编写文本操作算法!即使其他人已经做过这样的蠢事,也不要这样做!随它去!(嗯,有些情况下有意义......只需使用ICU库).
对于UCS-2编码的字符串使用std :: wstring(typedef std::wstring UCS2String
) - 这是一种妥协,并且是对WIN32 API引入的混乱的让步.UCS-2足以满足我们大多数人的需求(稍后会详细介绍......).
每当需要逐个字符访问(读取,操作等)时,都使用UCS2String实例.任何基于字符的处理都应以非多字节表示形式完成.它简单,快速,简单.
添加两个实用程序函数来在UTF-8和UCS-2之间来回转换:
UCS2String ConvertToUCS2( const UTF8String &str ); UTF8String ConvertToUTF8( const UCS2String &str );
转换很简单,谷歌应该在这里帮助...
而已.在内存珍贵的地方和所有UTF-8 I/O使用UTF8String.在必须解析和/或操作字符串的任何地方使用UCS2String.您可以随时在这两个表示之间进行转换.
替代方案和改进
转换为单字节字符编码(例如ISO-8859-1)可以借助普通转换表实现,例如const wchar_t tt_iso88951[256] = {0,1,2,...};
,用于转换到UCS2和从UCS2转换的适当代码.
如果UCS-2不够,那么切换到UCS-4(typedef std::basic_string
)
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的元素适量和逻辑字符.
是的,char总是至少8位长,这意味着它可以存储所有ASCII值.
是的,所有主要编译器都支持它.
我经常使用std :: string来保存utf-8字符而没有任何问题.我衷心地建议在与使用utf-8作为本机字符串类型的API接口时执行此操作.
例如,在将代码与Tcl解释器连接时,我使用utf-8.
主要的警告是std :: string的长度,不再是字符串中的字符数.