std :: wstring VS std :: string

时间:2008-12-31 04:08:14

标签: c++ string unicode c++-faq wstring

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

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

12 个答案:

答案 0 :(得分:943)

答案 1 :(得分:57)

我建议在Windows或其他地方避免使用std::wstring,除非接口需要,或者在Windows API调用附近的任何地方以及相应的编码转换作为语法糖。

我的观点总结在http://utf8everywhere.org中,我是其合着者。

除非您的应用以API呼叫为中心,例如主要是UI应用程序,建议是在std :: string中存储Unicode字符串并以UTF-8编码,在API调用附近执行转换。本文中概述的好处超过了转换的明显烦恼,特别是在复杂的应用程序中。对于多平台和库开发来说,这是双倍的。

现在,回答你的问题:

  1. 一些弱的原因。它存在是出于历史原因,其中宽带被认为是支持Unicode的正确方式。它现在用于接口更喜欢UTF-16字符串的API。我只在这些API调用的附近使用它们。
  2. 这与std :: string无关。它可以保存您放入的任何编码。唯一的问题是如何处理其内容。我的建议是UTF-8,因此它可以正确保存所有Unicode字符。这是Linux上的常见做法,但我认为Windows程序也应该这样做。
  3. 否。
  4. 宽字符是一个令人困惑的名字。在Unicode的早期,人们相信一个字符可以用两个字节编码,因此得名。今天,它代表“两个字节长的角色的任何部分”。 UTF-16被视为这种字节对的序列(又名宽字符)。 UTF-16中的字符需要一对或两对。

答案 2 :(得分:37)

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

我的实用结论非常简单:所有C ++(和STL)“字符编码”的内容都基本上是破碎和无用的。无论如何归咎于微软,无论如何都无济于事。

我的解决方案,经过深入调查,非常沮丧和相应的经历如下:

  1. 接受,你必须自己负责编码和转换的东西(你会发现其中很多都是微不足道的)

  2. 对任何UTF-8编码的字符串使用std :: string(只需typedef std::string UTF8String

  3. 接受这样一个UTF8String对象只是一个愚蠢但便宜的容器。永远不要直接访问和/或操纵其中的字符(不搜索,替换等)。你可以,但你真的真的,真的不想浪费你的时间为多字节字符串编写文本操作算法!即使其他人已经做过这样愚蠢的事情,也不要这样做!随它去! (嗯,有些情况下有意义......只需使用ICU库)。

  4. 对UCS-2编码的字符串(typedef std::wstring UCS2String)使用std :: wstring - 这是一个妥协,并且是对WIN32 API引入的混乱的让步。 UCS-2足以满足我们大多数人的需求(稍后会详细介绍......)。

  5. 每当需要逐个字符访问(读取,操作等)时,都使用UCS2String实例。任何基于字符的处理都应以非多字节表示形式完成。它简单,快速,简单。

  6. 添加两个实用功能以转换回&在UTF-8和UCS-2之间:

    UCS2String ConvertToUCS2( const UTF8String &str );
    UTF8String ConvertToUTF8( const UCS2String &str );
    
  7. 转换很简单,谷歌应该在这里提供帮助......

    就是这样。在内存珍贵的地方和所有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库?

    For advanced stuff.

答案 3 :(得分:25)

  1. 如果要在字符串中存储宽字符。 wide取决于实施。如果我没记错的话,Visual C ++默认为16位,而GCC默认值取决于目标。它的长度为32位。请注意wchar_t(宽字符类型)与unicode无关。它只是保证它可以存储实现由其语言环境支持的最大字符集的所有成员,并且至少与char一样长。您也可以使用std::string编码 unicode字符串存入utf-8。但它不会理解unicode代码点的含义。因此str.size()不会为您提供字符串中逻辑字符的数量,而只会提供存储在该字符串/ wstring中的char或wchar_t元素的数量。出于这个原因,gtk / glib C ++包装器开发了一个可以处理utf-8的Glib::ustring类。

    如果你的wchar_t是32位长,那么你可以使用utf-32作为unicode编码,你可以使用一个固定的存储处理unicode字符串(utf-32是固定长度)编码。这意味着你的wstring的s.size()函数将然后返回正确数量的wchar_t元素逻辑字符。

  2. 是的,char总是至少8位长,这意味着它可以存储所有ASCII值。
  3. 是的,所有主要编译器都支持它。

答案 4 :(得分:5)

我经常使用std :: string来保存utf-8字符而没有任何问题。我衷心地建议在与使用utf-8作为本机字符串类型的API接口时执行此操作。

例如,在将代码与Tcl解释器连接时,我使用utf-8。

主要警告是std :: string的长度,不再是字符串中的字符数。

答案 5 :(得分:3)

  1. 如果要存储“宽”(Unicode)字符。
  2. 是:其中255个(不包括0个)。
  3. 这是一篇介绍性文章:http://www.joelonsoftware.com/articles/Unicode.html

答案 6 :(得分:2)

仅满足256个不同字符的应用程序可以选择使用宽字符(超过8位)或可变长度编码(C ++术语中的多字节编码),例如UTF-8。宽字符通常比可变长度编码需要更多空间,但处理速度更快。处理大量文本的多语言应用程序在处理文本时通常使用宽字符,但在将其存储到磁盘时将其转换为UTF-8。

stringwstring之间的唯一区别是它们存储的字符的数据类型。一个字符串存储char,其大小保证至少为8位,因此您可以使用字符串进行处理,例如ASCII,ISO-8859-15或UTF-8文本。该标准没有说明字符集或编码。

实际上,每个编译器都使用一个字符集,其前128个字符对应ASCII。使用UTF-8编码的编译器也是如此。在UTF-8或其他可变长度编码中使用字符串时要注意的重要事项是索引和长度以字节为单位,而不是字符。

wstring的数据类型是wchar_t,其大小未在标准中定义,除了它必须至少与char一样大,通常是16位或32位。 wstring可用于处理实现定义的宽字符编码中的文本。由于编码未在标准中定义,因此在字符串和字符串之间进行转换并不简单。人们不能假设wstrings也有固定长度的编码。

如果您不需要多语言支持,那么只使用常规字符串就可以了。另一方面,如果您正在编写图形应用程序,则通常情况下API仅支持宽字符。然后,您可能希望在处理文本时使用相同的宽字符。请记住,UTF-16是一种可变长度编码,这意味着您不能假设length()返回字符数。如果API使用固定长度编码,例如UCS-2,则处理变得容易。在宽字符和UTF-8之间转换很难以便携方式进行,但是再次,您的用户界面API可能支持转换。

答案 7 :(得分:1)

  1. 当你想使用Unicode字符串而不仅仅是ascii时,有助于国际化
  2. 是的,但它与0
  3. 的效果不佳
  4. 不知道任何不
  5. 宽字符是处理unicode字符的固定长度表示的编译器特定方式,对于MSVC,它是2字节字符,对于gcc,我理解它是4字节。和http://www.joelonsoftware.com/articles/Unicode.html
  6. 的+1

答案 8 :(得分:0)

1)正如Greg所说,wstring有助于国际化,那就是当你用英语以外的语言发布你的产品时

4)检查这个是否有广泛的性格 的 http://en.wikipedia.org/wiki/Wide_character

答案 9 :(得分:0)

一个好问题! 我认为 DATA ENCODING (有时还涉及 CHARSET )是 MEMORY EXPRESSION 机制,以便将数据保存到文件或通过数据传输数据网络,所以我回答这个问题:

<强> 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&amp; * _&amp;”并且你说特别的,大多数打印为'。'避免混淆编辑或终端。其他一些国家扩展了他们自己的“ASCII”字符集,例如中文,使用2个八位字节代表一个字符。

3.所有流行的C ++编译器都支持std :: wstring吗?

可能,或者主要是。我用过:VC ++ 6和GCC 3.3,是

<强> 4。什么是“广泛的角色”?

宽字符主要表示使用2个八位字节或4个八位字节来保存所有国家/地区的字符。 2个八位字节UCS2是代表性样本,并且进一步例如英语'a',其内存为2个字节的0x0061(vs。在ASCII'a的内存为1个八位字节0x61)

答案 10 :(得分:0)

这里有一些很好的答案,但是我认为我可以添加一些有关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中进行编码,但是您也可能没有使用宽字符串文字。

std :: cout

使用<<输出到控制台时,只能使用std::string,不能使用std::wstring,并且必须使用区域设置代码页对文本进行编码。如果您有std::wstring,则必须使用Windows API函数之一对其进行转换,并且代码页上未包含的任何字符都将被?替换(也许您可以更改字符,我不记得了)

std :: fstream文件名

Windows操作系统使用UCS2 / UTF-16作为其文件名,因此无论您的代码页如何,您都可以使用带有任何Unicode字符的文件。但这意味着要访问或创建代码页上未包含字符的文件,必须使用std::wstring。没有别的办法了。这是Microsoft对std::fstream的扩展,因此可能不会在其他系统上编译。如果您使用std :: string,则只能使用在代码页上仅包含字符的文件名。

您的选项

如果您只是在Linux上工作,那么您可能还没走得那么远。只需在各处使用UTF-8 std::string

如果您仅在Windows上工作,则可在任何地方使用UCS2 std::wstring。一些纯粹主义者可能会说使用UTF8然后在需要时进行转换,但是为什么要麻烦呢。

坦率地说,如果您是跨平台的,那是一团糟。如果您尝试在Windows的任何地方使用UTF-8,则需要非常小心地使用字符串文字并将其输出到控制台。您可以在此轻松破坏字符串。如果您在Linux上的所有地方都使用std::wstring,则可能无法访问std::fstream的宽版本,因此您必须进行转换,但是没有损坏的风险。所以我个人认为这是一个更好的选择。许多人会不同意,但我并不孤单-例如,这就是wxWidgets采取的路径。

另一种选择是将unicodestring在Linux上键入为std::string,在Windows上键入std::wstring,并具有一个称为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

的'std :: basic_string'的简单typedef

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

答案 11 :(得分:-5)

什么时候不应该使用宽字符?

当你在1990年之前编写代码时。

显然,我正在翻身,但实际上,现在是21世纪。 127个字符早已不复存在。是的,你可以使用UTF8,但为什么还要烦恼呢?