std :: wstring VS std :: string


740

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

  1. 我什么时候应该std::wstring用完std::string
  2. 可以std::string容纳整个ASCII字符集,包括特殊字符吗?
  3. std::wstring由所有流行的C ++编译器的支持?
  4. 什么是“ 宽字符 ”?

10
ASCII字符集没有很多“特殊”字符,最奇特的可能是`(反引号)。std :: string可以容纳大约0.025%的所有Unicode字符(通常为8位字符)
MSalters 2009年

3
大约宽字符和哪种类型的使用良好的信息可以在这里找到:programmers.stackexchange.com/questions/102205/...
Yariv

14
好吧,因为我们在2012年,所以写了utf8everywhere.org。它几乎回答了有关C ++ / Windows是非问题的所有问题。
帕维尔·拉兹维洛夫斯基

42
@MSalters:std :: string可以容纳所有Unicode字符的100%,即使CHAR_BIT为8也是如此。这取决于std :: string的编码,在系统级别上它可能是UTF-8(就像Windows以外几乎所有地方一样) )或您的应用程序级别。本机窄编码不支持Unicode?没问题,只是不要使用它,而是使用UTF-8。
Yakov Galka 2012年

8
关于这个主题的精彩
Timothy Shields

Answers:


989

stringwstring

std::stringbasic_stringcharstd::wstring上的模板wchar_t

charwchar_t

char应该包含一个字符,通常是8位字符。
wchar_t应该具有宽字符,然后事情变得棘手:
在Linux上,a wchar_t为4字节,而在Windows上为2字节。

那么Unicode呢?

问题是既没有char也不wchar_t直接与unicode绑定。

在Linux上?

让我们以Linux操作系统为例:我的Ubuntu系统已经支持Unicode。当我使用char字符串时,它是本地编码为UTF-8(即Unicode的char字符串)。如下代码:

#include <cstring>
#include <iostream>

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<unsigned int>(
                              static_cast<unsigned char>(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<unsigned int>(
                              static_cast<unsigned short>(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一起使用时charstd::string已经可以使用unicode了。

请注意std::string,与C字符串API一样,它将认为“olé”字符串具有4个字符,而不是3个字符。因此,在截断/播放unicode字符时,请务必谨慎,因为UTF-8中禁止使用某些字符组合。

在Windows上?

在Windows上,这有点不同。在Unicode出现之前,Win32必须支持许多应用程序,这些应用程序可以char与世界各地生产的不同字符集 / 代码页一起使用,并可以在这些字符集 / 代码页上使用。

因此,他们的解决方案是一个有趣的解决方案:如果应用程序使用char,则使用机器上的本地字符集/代码页将字符字符串编码/打印/显示在GUI标签上。例如,在法语本地化的Windows中,“olé”将是“olé”,但是在西里尔语本地化的Windows中,“olé”将有所不同(如果使用Windows-1251,则为“olй” )。因此,“历史应用程序”通常仍将以相同的旧方式工作。

对于基于Unicode的应用程序,Windows使用wchar_t2字节宽的,并以UTF-16编码,而UTF-16是2字节字符的Unicode编码(或者至少是最兼容的UCS-2,几乎是UCS-2 IIRC)。

使用char的应用程序称为“多字节”(因为每个字形由一个或多个chars组成),而使用wchar_t的应用程序称为“ widechar”(因为每个字形由一个或两个组成wchar_t。有关更多信息,请参见MultiByteToWideCharWideCharToMultiByte Win32转换API。

因此,如果您在Windows上工作,则极需要使用wchar_t(除非您使用隐藏了该框架的框架,例如GTK +QT ...)。事实是,在幕后,Windows使用wchar_t字符串,因此即使使用API (在Win32 GUI上设置标签的低级API函数),即使是历史应用程序也将char转换其字符串。wchar_tSetWindowText()

内存问题?

UTF-32是每个字符4个字节,因此,如果仅UTF-8文本和UTF-16文本将始终比UTF-32文本使用更少或相同的内存量(通常更少),则无需添加太多内容)。

如果存在内存问题,那么您应该比大多数西方语言都知道,与相同的UTF-16相比,UTF-8文本将使用更少的内存。

但是,对于其他语言(中文,日文等),与UTF-16相比,UTF-8使用的内存将相同或稍大。

总而言之,UTF-16每个字符最多使用2个字节,有时使用4个字节(除非您要处理某种深奥的语言字形(Klingon?Elvish?),而UTF-8则使用1到4个字节。

有关更多信息,请参见http://en.wikipedia.org/wiki/UTF-8#Compared_to_UTF-16

结论

  1. 什么时候应该在std :: string上使用std :: wstring?

    在Linux上?几乎从不 (§)。
    在Windows上?几乎总是 (§)。
    在跨平台代码上?取决于您的工具箱...

    (§):除非您使用工具箱/框架,否则

  2. 可以std::string容纳所有ASCII字符集,包括特殊字符吗?

    注意:A std::string适合于容纳“二进制”缓冲区,而a std::wstring不适合!

    在Linux上?是。
    在Windows上?仅特殊字符可用于Windows用户的当前区域设置。

    编辑(在Johann Gerell发表评论之后):
    a std::string足以处理所有char基于字符串的字符串(每个字符串char都是0到255之间的数字)。但:

    1. ASCII应该从0到127。更高的chars不是ASCII。
    2. 一个char从0到127将被正确举行
    3. 一个char128到255之间的符号将取决于您的编码(unicode,non-unicode等),但是只要它们以UTF-8编码,它就可以保存所有Unicode字形。
  3. std::wstring几乎所有流行的C ++编译器的支持?

    通常,除了已移植到Windows的基于GCC的编译器。
    它适用于我的g ++ 4.3.2(在Linux下),并且自Visual C ++ 6起,我就在Win32上使用了Unicode API。

  4. 什么是宽字?

    在C / C ++上,它是一种wchar_t比简单char字符类型大的字符类型。它应该用于放入索引大于255(或127,取决于...)的字符(如Unicode字形)。


4
@gnud:在UTF-16出现之前,也许wchar_t应该足以处理所有UCS-2字符(大多数UTF-16字符)...或者Microsoft确实具有POSIX以外的其他优先级,例如可以轻松访问Unicode。而不修改Win32上char的codepaged使用。
paercebal

4
@Sorin Sbarnea:UTF-8可以占用1-6个字节,但显然标准将其限制为1-4个字节。有关更多信息,请参见en.wikipedia.org/wiki/UTF8#Description
paercebal 2010年

8
尽管此示例在Linux和Windows上产生不同的结果,但C ++程序包含有关是否olè编码为UTF-8的实现定义的行为。更进一步,您无法本地传输wchar_t *到的原因std::cout是因为类型不兼容,导致程序格式错误,并且与编码的使用无关。值得指出的是,是使用std::string还是std::wstring取决于自己的编码首选项而不是平台,特别是如果您希望代码可移植。
约翰·莱德格伦

14
Windows实际上使用UTF-16,并且已经使用了相当长的一段时间,较早版本的Windows确实使用UCS-2,但是现在不再是这种情况。我在这里唯一的问题是std::wstring应该在Windows上使用的结论,因为它更适合Unicode Windows API,我认为这是错误的。如果您唯一关心的是调用Unicode Windows API而不是编组字符串,那么可以肯定,但是我不一般购买。
约翰·莱德格伦

15
// @约翰·莱德格伦::If your only concern was calling into the Unicode Windows API and not marshalling strings then sure那,我们同意。我使用C ++而不是JavaScript进行编码。该语言的核心是避免在运行时进行无用的编组或任何其他潜在的昂贵处理,这是该语言的核心。针对WinAPI进行编码和使用std::string只是浪费了运行时资源,这是不合理的。您会发现它很谬误,没关系,因为这是您的观点。我自己的观点是,我不会因为在Linux方面看起来更好而在Windows上使用悲观主义编写代码。
paercebal,2012年

71

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

我的共同作者http://utf8everywhere.org中概述了我的观点。

除非您的应用程序以API调用为中心,例如主要是UI应用程序,否则建议将Unicode字符串存储在std :: string中并以UTF-8编码,在API调用附近执行转换。本文概述的好处远远超过了转换带来的烦恼,尤其是在复杂的应用程序中。对于多平台和库开发而言,这是双重的。

现在,回答您的问题:

  1. 一些弱的原因。它是出于历史原因而存在的,人们认为Widechars是支持Unicode的正确方法。现在,它用于连接喜欢UTF-16字符串的API。我仅在此类API调用的附近使用它们。
  2. 这与std :: string无关。它可以保存您输入的任何编码。唯一的问题是如何对待其内容。我的建议是UTF-8,因此它将能够正确容纳所有Unicode字符。这是Linux上的常见做法,但我认为Windows程序也应该这样做。
  3. 没有。
  4. 宽字符是一个令人困惑的名称。在Unicode的早期,人们认为可以将一个字符编码为两个字节,因此可以命名为一个字节。今天,它代表“字符的任何部分,长度为两个字节”。UTF-16被视为此类字节对(也称为宽字符)的序列。UTF-16中的一个字符需要一对或两对。

37

因此,现在这里的每个读者都应该对事实,情况有清楚的了解。如果不是,那么您必须阅读paercebal出色的综合答案 [btw:谢谢!]。

我的务实结论非常简单:所有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实例。任何基于字符的处理都应以NON-multibyte表示形式进行。它简单,快速,容易。

  6. 添加两个实用程序函数以在UTF-8和UCS-2之间来回转换:

    UCS2String ConvertToUCS2( const UTF8String &str );
    UTF8String ConvertToUTF8( const UCS2String &str );

转换非常简单,Google应该在这里提供帮助...

而已。在内存很宝贵的地方和所有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库?

对于高级的东西。


当,知道本地Unicode支持不存在是不好的。
Mihai Danila 2013年

@Frunsi,我很好奇您是否尝试过Glib :: ustring,如果这样,您有什么想法?
卡罗琳·贝尔特兰

@CarolineBeltran:我知道Glib,但是我从未使用过它,而且我可能甚至不会使用它,因为它仅限于一个相当具体的目标平台(unixoid系统...)。它的Windows端口基于外部win2unix层,而IMHO根本没有OSX兼容层。至少对于我的代码(在此体系结构级别上……),所有这些东西显然都在朝着错误的方向发展;-)因此,Glib不是一个选择
Frunsi 2014年

9
搜索,替换等在UTF-8字符串上都可以正常工作(表示字符的字节序列的一部分永远不会被误解为另一个字符)。实际上,UTF-16和UTF-32一点都不容易:实际上,所有三种编码都是多字节编码,因为用户感知的字符(字素簇)可以是任意数量的unicode码点!务实的解决方案是对所有内容都使用UTF-8,并且仅在处理Windows API时才转换为UTF-16。
丹尼尔(Daniel)

5
@Frunsi:使用UTF-8进行搜索和替换的效果与使用UTF-32一样好。正是因为正确的Unicode感知文本处理无论如何都需要处理多代码点“字符”,所以使用像UTF-8这样的可变长度编码不会使字符串处理变得更加复杂。因此,只需在各处使用UTF-8。普通的C字符串函数可以在UTF-8上正常工作(并与Unicode字符串上的序数比较相对应),并且,如果您需要更多语言支持,则无论如何都必须调用Unicode库,即UTF-16 / 32不能拯救你。
丹尼尔(Daniel)

25
  1. 当您想在字符串中存储宽字符时。wide取决于实现。如果我没记错的话,Visual C ++默认为16位,而GCC默认取决于目标。这里是32位长。请注意,wchar_t(宽字符类型)与Unicode无关。仅保证它可以存储实现由其语言环境支持的最大字符集的所有成员,至少与char一样长。您也可以使用编码存储 unicode字符串。但是它不理解unicode代码点的含义。所以std::stringutf-8str.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的元素适量逻辑字符。

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

我对#2感到好奇。我以为7位在技术上也有效?还是需要能够存储超过7位ASCII字符的任何内容?
jalf

1
是的,杰夫。c89在其limits.h文档中指定了基本类型的最小范围(对于无符号字符,为0..255分钟),并且为整数类型指定了纯二进制系统。它遵循char,unsigned char和signed char的最小位长为8。c ++继承了这些规则。
Johannes Schaub-litb

15
“这意味着您的wstring的s.size()函数将返回正确数量的wchar_t元素和逻辑字符。” 即使对于Unicode,这也不是完全准确的。说代码点比“逻辑字符”更为准确,即使在UTF-32中,给定字符也可能由多个代码点组成。
洛根·卡帕尔多

你们在本质上是在说C ++不支持Unicode字符集吗?
Mihai Danila 2013年

1
“但是它不会理解unicode代码点的含义。” 在Windows上,都不会std::wstring
Deduplicator 2015年

5

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

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

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


1
Juan:您是说std​​ :: string可以容纳所有unicode字符,但是长度报告错误?是否有报告长度不正确的原因?

3
使用utf-8编码时,单个unicode字符可能由多个字节组成。这就是为什么在大多数情况下使用标准ascii集中的字符时utf-8编码较小的原因。您需要使用特殊功能(或滚动自己的功能)来测量unicode字符的数量。

2
(特定于Windows)大多数功能都希望使用字节的字符串是ASCII,而2个字节是Unicode(旧版本MBCS)。这意味着,如果要存储8位unicode,则必须将其转换为16位unicode才能调用标准的Windows函数(除非仅使用ASCII部分)。
格雷格·多姆詹

2
std :: string不仅会错误地报告长度,而且还会输出错误的字符串。如果某些Unicode字符在UTF-8中以多个字节表示,而std :: string则将其视为自己的字符,那么通常的std :: string操作例程可能会输出由于误解一个字符而导致的几个奇怪字符。正确的字符。
Mihai Danila 2013年

2
我建议更改答案以指示字符串应仅被视为字节的容器,并且,如果字节是某些Unicode编码(UTF-8,UTF-16等),则应使用特定的库来理解那。基于标准字符串的API(长度,substr等)都将因多字节字符而失败。如果进行了此更新,我将删除我的弃权票。
Mihai Danila 2014年

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

11
std :: string可以很好地容纳0(请小心,如果调用c_str()方法)
Fooz先生08年

3
严格来说,不能保证char为8位。:)您在#4中的链接是必读的,但我认为它不能回答问题。宽字符与unicode完全无关。它只是一个更广泛的角色。(宽度的大小取决于操作系统,但通常为16或32位)
jalf

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

1
2. std :: string可以很好地容纳NULL字符。它还可以容纳utf-8和宽字符。

@Juan:那又让我感到困惑。如果std :: string可以保留unicode字符,那么std :: wstring有何特殊之处?

1
@Appu:std :: string可以容纳UTF-8 Unicode字符。有许多针对不同字符宽度的unicode标准。UTf8为8位宽。还有UTF-16和UTF-32,分别为16位和32位宽
Greg D,

使用std :: wstring。使用固定长度编码时,每个unicode字符可以是一个wchar_t。例如,如果您选择使用Gel链接到的Joel on Software方法。那么wstring的长度恰好是字符串中的unicode字符数。但是它占用了更多空间

我并不是说它不能容纳0'\ 0',而我的意思是不能很好地发挥作用,因为某些方法可能无法为您提供包含wstring所有数据的预期结果。如此严厉的否决票。
格雷格·多姆詹

2

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

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

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

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

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


因此,将第一段解释为:需要超过256个字符的应用程序需要使用多字节编码或maybe_multibyte编码。
Deduplicator 2015年

但是,通常不将16位和32位编码(例如UCS-2和UCS-4)称为多字节编码。C ++标准区分多字节编码和宽字符。宽字符表示每个字符使用固定数量(通常超过8个)的位。使用单个字节编码最常见字符,而使用多个字节编码其余字符集的编码称为多字节编码。
Seppo Enarvi 2015年

对不起,草率的评论。应该说可变长度编码。就像UTF-8一样,UTF-16是可变长度编码。假装不是一个主意。
Deduplicator 2015年

那是个很好的观点。没有理由不能将wstrings用于存储UTF-16(而不是UCS-2),但是这样就失去了固定长度编码的便利性。
Seppo Enarvi 2015年

2

一个好问题!我认为数据编码(有时也涉及CHARSET)是一种内存表达机制,目的是将数据保存到文件或通过网络传输数据,因此我以以下方式回答此问题:

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&* _&”,您说的是特殊字符,通常将其打印为'。'。避免混淆编辑器或终端。还有一些其他国家/地区扩展了自己的“ ASCII”字符集,例如中文,使用2个八位位组代表一个字符。

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

也许,或者大部分。我曾经使用过:VC ++ 6和GCC 3.3,是的

4.什么是“宽字符”?

宽字符主要表示使用2个八位字节或4个八位字节来保存所有国家/地区的字符。2个八位位组UCS2是一个代表性示例,例如英语'a',其内存为0x0061的2个八位位组(相对于ASCII'a's的内存为1个八位位组0x61)


0

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

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

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

另一种选择可能是typedef定义unicodestringstd::string在Linux和std::wstringWindows上,并有一个叫做UNI()在Linux宏观其在Windows前缀L和什么都没有,然后代码

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

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


1
关于“但是,VS在将源传递给编译器之前,VS将UTF-8编码的文本转换为代码页编码的文本,并且代码页中缺少的任何字符都替换为?”。->当编译器使用UTF-8编码(使用/utf-8)时,我认为这不是真的。
罗伊·丹顿

我不知道这是一个选择。从此链接docs.microsoft.com/zh-cn/cpp/build/reference/…似乎在项目属性中没有选择框,您必须将其添加为其他命令行选项。好地方!
Phil Rosenberg


-6

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

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

显然,我正在翻转,但实际上,现在是21世纪。127个字符早就不再足够了。是的,您可以使用UTF8,但是为什么要烦恼头痛呢?


16
@dave:我不知道UTF-8会引起什么头痛,这比Widechars(UTF-16)的头痛更大。在UTF-16中,您还具有多个字符。
帕维尔·拉兹维洛夫斯基

问题是,如果您位于英语国家/地区以外的其他国家,则应该使用wchar_t。更不用说某些字母的字符数超出了您可以容纳的字节数。我们在DOS上。代码页精神分裂症,不,不用了,谢谢。
Swift-Friday Pie

1
@Swift问题wchar_t在于它的大小和含义是特定于OS的。它只是将旧问题换成新问题。而a char是独立char于操作系统的(至少在类似平台上)。因此,我们不妨使用UTF-8,将所有内容打包到chars 序列中,并感叹C ++如何完全依靠我们自己,而无需在这些序列中进行任何测量,索引,查找等标准方法。
underscore_d

1
@Swift您似乎已经完全倒退了。wchar_t是固定宽度的数据类型,因此10个数组wchar_t将始终占据sizeof(wchar_t) * 10平台字节。UTF-16是一种可变宽度编码,其中的字符可以由1个或2个16位代码点组成(对于UTF-8,则为s / 16/8 / g)。
underscore_d

1
@SteveHollasch Windows上的字符串wchar_t表示形式会将大于FFFF的字符编码为特殊代理对,其他字符仅采用一个wchar_t元素。因此,该表示形式将与gnu编译器创建的表示形式不兼容(所有小于FFFF的字符前面都有零个字)。wchar_t中存储的内容由程序员和编译器决定,而不是由某些协议决定
Swift-Friday Pie
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.