我无法理解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吗?什么是“宽字符”?
当前回答
我建议避免在Windows或其他地方使用std::wstring,除非接口需要,或者在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中的字符采用一对或两对。
其他回答
这里有一些非常好的答案,但我认为关于Windows/Visible Studio,我可以补充一些内容。这是基于我在VS2015的经验。在Linux上,基本上答案是到处使用UTF-8编码的std::string。在Windows/VS上,它变得更加复杂。原因如下。Windows希望使用区域设置代码页对使用字符存储的字符串进行编码。这几乎总是ASCII字符集,后跟128个其他特殊字符,具体取决于您的位置。让我声明,这不仅仅是在使用WindowsAPI时,还有三个主要的地方,这些字符串与标准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::字符串中,但也可以不使用宽字符串文本。
标准::cout
当使用<<输出到控制台时,只能使用std::string,而不是std::wstring,并且必须使用区域设置代码页对文本进行编码。如果您有std::wstring,则必须使用一个Windows API函数转换它,并且代码页上没有的字符将被替换为?(也许你可以改变角色,我不记得了)。
std::fstream文件名
Windows操作系统使用UCS2/UTF-16作为文件名,因此无论您的代码页是什么,您都可以使用任何Unicode字符的文件。但这意味着要访问或创建包含不在代码页上的字符的文件,必须使用std::wstring。没有其他办法。这是对std::fstream的Microsoft特定扩展,因此可能不会在其他系统上编译。如果使用std::string,则只能使用代码页上只包含字符的文件名。
您的选项
如果你只是在Linux上工作,那么你可能还没有走到这一步。只需在任何地方使用UTF-8 std::string。
如果您只是在Windows上工作,请在任何地方使用UCS2 std::wstring。一些纯粹主义者可能会说,使用UTF8然后在需要时进行转换,但为什么要麻烦呢。
如果你是跨平台的,那么坦率地说是一团糟。如果您尝试在Windows上到处使用UTF-8,那么您需要非常小心地处理字符串文本和输出到控制台。你很容易在那里破坏你的字符串。如果您在Linux上到处使用std::wstring,那么您可能无法访问std::fstream的广泛版本,因此您必须进行转换,但没有损坏的风险。所以我个人认为这是一个更好的选择。很多人会不同意,但我并不孤单——比如wxWidgets就走了这条路。
另一个选项可以是在Linux上将unicodestring类型定义为std::string,在Windows上将std::wstring,并有一个名为UNI()的宏,该宏在Windows上前缀为L,在Linux上前缀为nothing,然后代码
#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。但是WindowsAPI和标准C++类将期望使用语言环境代码页对std::字符串进行编码。这包括所有ASCII码加上其他128个字符,这些字符根据计算机设置使用的代码页而变化。
3) 我相信是这样,但如果不是,那么它只是使用wchar_t而不是char的“std::basic_string”的简单typedef
4) 宽字符是大于1字节标准字符类型的字符类型。在Windows上是2字节,在Linux上是4字节。
我建议避免在Windows或其他地方使用std::wstring,除非接口需要,或者在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中的字符采用一对或两对。
一个好问题!我认为数据编码(有时还涉及字符集)是一种内存表达机制,用于将数据保存到文件或通过网络传输数据,因此我将这个问题回答为:
1.何时应该使用std::wstring而不是std::string?
如果编程平台或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”的内存为1个八位位0x61)
1) 正如Greg所提到的,wstring有助于国际化,这意味着您将以英语以外的语言发布产品
4) 检查此项以获取宽字符http://en.wikipedia.org/wiki/Wide_character
什么时候不应该使用宽字符?
当你在1990年之前编写代码时。
很明显,我很生气,但事实上,现在是21世纪。127个字符早已不再足够。是的,你可以使用UTF8,但为什么要头疼呢?