我无法理解std::string和std::wstring之间的区别。我知道wstring支持Unicode字符等宽字符。我有以下问题:

什么时候应该使用std::wstring而不是std::string?std::string可以保存整个ASCII字符集,包括特殊字符吗?所有流行的C++编译器都支持std::wstring吗?什么是“宽字符”?


当前回答

当您希望在字符串中存储宽字符时。宽取决于实现。如果我没记错的话,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值。是的,所有主要的编译器都支持它。

其他回答

当您希望在字符串中存储宽字符时。宽取决于实现。如果我没记错的话,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::字符串的长度,不再是字符串中的字符数。

这里有一些非常好的答案,但我认为关于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字节。

所以,现在在座的每一位读者都应该清楚地了解事实和情况。如果没有,那么你必须阅读帕塞巴尔非常全面的回答[顺便说一句:谢谢!]。

我的务实结论非常简单:所有C++(和STL)“字符编码”的东西基本上都是残缺不全的。不管是否归咎于微软,这都无济于事。

经过深入调查后,我的解决方案是:

接受,你必须自己负责编码和转换的事情(你会发现很多事情都很琐碎)对任何UTF-8编码字符串使用std::string(仅为typedef std::字符串UTF8String)接受这样一个UTF8String对象只是一个愚蠢但廉价的容器。永远不要直接访问和/或操作其中的字符(不要搜索、替换等)。你可以,但你真的不想浪费时间为多字节字符串编写文本操作算法!即使其他人已经做了如此愚蠢的事情,也不要这样做!顺其自然!(好吧,在某些情况下,这是合理的……只需使用ICU图书馆即可)。对UCS-2编码字符串使用std::wstring(typedef std::wstring UCS2String)-这是一种妥协,也是对WIN32 API引入的混乱的让步)。UCS-2对我们大多数人来说已经足够了(稍后将详细介绍…)。每当需要逐字符访问(读取、操作等)时,请使用UCS2String实例。任何基于字符的处理都应该在非多字节表示中完成。它简单、快速、容易。添加两个实用函数以在UTF-8和UCS-2之间来回转换:UCS2字符串转换为UCS2(const UTF8String&str);UTF8字符串转换为UTF8(常量UCS2String&str);

转换很简单,谷歌应该在这里提供帮助。。。

就是这样。在内存非常宝贵的地方以及所有UTF-8 I/O都使用UTF8String。在必须解析和/或操作字符串的地方使用UCS2String。您可以随时在这两种表示之间进行转换。

替代方案和改进

从&到单字节字符编码(例如ISO-8859-1)的转换可以借助于普通转换表来实现,例如const wchar_ttt_iso88951[256]={0,1,2,…};以及用于转换到UCS2和从UCS2转换的适当代码。如果UCS-2不够,则切换到UCS-4(typedef std::basic_string<uint32_t>UCS2String)

ICU或其他unicode库?

先进的东西。

1) 正如Greg所提到的,wstring有助于国际化,这意味着您将以英语以外的语言发布产品

4) 检查此项以获取宽字符http://en.wikipedia.org/wiki/Wide_character