std::wstring VS std::string

新手上路,请多包涵

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

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

原文由 Appu 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 690
1 个回答

这里有一些非常好的答案,但我认为我可以添加一些关于 Windows/Visual Studio 的内容。这是基于我对 VS2015 的经验。在 Linux 上,基本上答案是到处使用 UTF-8 编码的 std::string 。在 Windows/VS 上,它变得更加复杂。这就是为什么。 Windows 期望使用 char 存储的字符串使用区域设置代码页进行编码。这几乎总是 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 中,但是您可能还没有使用宽字符串文字。

标准::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 上键入def unicodestring as std::stringstd::wstring 在 Windows 上,并且在 Windows 上没有名为 UNI() 的宏,在 Windows 上没有前缀 L Linux,然后是代码

#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)我相信是这样,但如果不是,那么它只是使用 wchar_t 而不是 char 的简单 typedef ‘std::basic_string’

4)宽字符是大于1字节标准的字符类型 char 类型。在 Windows 上是 2 个字节,在 Linux 上是 4 个字节。

原文由 Phil Rosenberg 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题