如何获得STL std :: string在Windows上使用Unicode?

在我的公司,我们有一个包含我们自己的STL std :: string扩展的跨平台(Linux&Windows)库,这个类提供了string顶部的所有function; 拆分,格式,到/从base64等等。最近我们被要求使这个stringunicode“友好”,基本上它需要支持来自中文,日文,阿拉伯文等字符。经过初步的研究,这似乎在Linux端因为每个东西本质上都是UTF-8,但是我在Windows端遇到了麻烦。 有没有一个技巧让STL std :: string在Windows上以UTF-8工作? 这甚至有可能吗? 有没有更好的办法? 理想情况下,我们会保持自己基于std :: string,因为这是string类在Linux中的基础。

谢谢,

你的问题有几个误解。

  • C ++和STL都不处理编码。

  • std::string本质上是一个字节的字符串,而不是字符 。 所以你应该没有问题填入UTF-8编码的Unicode。 但是,请记住,所有string函数也可以在字节上工作,所以myString.length()会给你字节数,而不是字符数。

  • Linux本身并不是 UTF-8。 现在的大部分发行版默认为UTF-8,但不应该依赖它。

是的 – 通过更多地了解语言环境和编码。

Windows对于需要文本的所有东西都有两个函数调用,FoobarA()和FoobarW()。 * W()函数采用UTF-16编码的字符串,* A()采用当前代码页中的字符串。 但是,Windows不支持UTF-8代码页,因此您不能直接在* A()函数中使用它,也不想依赖于由用户设置的代码页。 如果您想在Windows中使用“Unicode”,请使用支持Unicode的(* W)功能。 那里有教程,谷歌搜索“Unicode Windows教程”应该给你一些。

如果您将UTF-8数据存储在std :: string中,那么在将它传递给Windows之前,将其转换为UTF-16(Windows提供这样的功能),然后将它传递给Windows。

C / C ++通常是编码不可知的,其中许多问题都是由此产生的。 char不是一个真正的字符,它只是一个整数类型。 即使使用char数组来存储UTF-8数据,如果您需要访问各个代码单元,也可能会遇到麻烦,因为char的签名没有被标准定义。 像str[x] < 0x80这样的语句检查多字节字符可以很快引入一个错误。 (如果char被签名,该语句总是为真。)UTF-8代码单元是一个范围为0-255的无符号整数类型。 这完全映射到uint8_t的C类型,尽管unsigned char也可以。 理想情况下,我会做一个UTF-8字符串uint8_t的数组,但由于旧的API,这很少做。

有些人推荐wchar_t ,声称它是“一个Unicode字符类型”或类似的东西。 同样,这里的标准和以前一样是不可知论的,因为C可以在任何地方工作,而且任何地方都可能不使用Unicode。 因此, wchar_t没有比char更多的Unicode。 标准规定:

它是一个整数类型,其值范围可以表示支持的语言环境中指定的最大扩展字符集的所有成员的不同代码

在Linux中, wchat_t表示UTF-32代码单元/代码点。 这是4个字节。 但是,在Windows中,它是一个UTF-16编码单元,只有2个字节。 (我认为这不符合上面的内容,因为2字节不能代表所有的Unicode,但这是它的工作方式)。这种尺寸差异和数据编码的差异明显地增加了可移植性。 如果您需要可移植性,Unicode标准本身建议不要使用wchar_t 。 (第5.2节)

最后一课:我发现最简单的方法是将所有的数据以一种明确的格式存储起来。 (通常UTF-8,通常在std :: string的,但我真的很喜欢的东西。)这里重要的不是UTF-8部分,而是我知道我的字符串是UTF-8。 如果我把它们传递给其他API,我还必须知道该API需要UTF-8字符串。 如果没有,那么我必须转换它们。 (因此,如果我对Window的API说话,我必须首先将字符串转换为UTF-16。)UTF-8文本字符串是一个“橙色”,“latin1”文本字符串是一个“苹果”。 不知道它是什么编码的char数组是一个灾难的秘诀。

将UTF-8代码点放入std::string应该没问题,不管平台如何。 Windows上的问题几乎没有其他的东西可以用UTF-8来处理,而是使用UTF-16。 你可以切换到一个std::wstring来存储UTF-16(至少在大多数的Windows编译器上),或者你可以编写其他接受UTF-8的例程(可能是转换为UTF-16,然后传递给OS)。

你看过std::wstring吗? 它是wchar_tstd::basic_string版本,而不是std::string使用的std::string

不,没有办法让Windows将“窄”字符串视为UTF-8。

在这种情况下,这是最适合我的方式(具有Windows和Linux版本的跨平台应用程序)。

  • 在代码的跨平台部分使用std :: string。 假设它总是包含UTF-8字符串。
  • 在代码的Windows部分中,显式使用“宽”版本的Windows API,即编写例如CreateFileW而不是CreateFile。 这样可以避免依赖构建系统配置。
  • 在平台抽象层,在需要的地方(MultiByteToWideChar / WideCharToMultiByte)在UTF-8和UTF-16之间进行转换。

其他的方法,我尝试,但不喜欢很多:

  • typedef std::basic_string<TCHAR> tstring; 然后在业务代码中使用tstring。 可以使用包装器/重载来简化std :: string和std :: tstring之间的转换,但是它仍然会增加很多痛苦。
  • 无处不在使用std::wstring 。 由于wchar_t在Windows上是16位的,所以没有太多的帮助,所以你必须限制自己的BMP,或者做很多复杂的事情来使代码处理Unicode的跨平台。 在后一种情况下,UTF-8的所有优势都会消失。
  • 在平台特定部分使用ATL / WTL / MFC CString ; 在交叉平台部分使用std::string 。 这实际上是我上面推荐的一个变种。 CString在许多方面优于std::string (在我看来)。 但它引入了额外的依赖性,因此并不总是可以接受的或方便的。

如果您想避免头痛,请不要使用STL字符串类型。 C ++对Unicode或编码一无所知,因此为了便于携带,最好使用专为Unicode支持而定制的库,例如ICU库。 ICU默认使用UTF-16字符串,因此不需要转换,并且支持转换为许多其他重要的编码,如UTF-8。 也尝试使用像Boost.Filesystem这样的跨平台库来处理路径操作( boost::wpath )。 避免std::stringstd::fstream

在Windows API和C运行时库中, char*参数被解释为在“ANSI”代码页中被编码。 问题是UTF-8不支持作为一个ANSI代码页 , 我觉得令人难以置信的烦人 。

我处于类似的情况,处于从Windows到Linux的软件移植过程中,同时也使其能够识别Unicode。 我们采取的方法是:

  • 使用UTF-8作为字符串的默认编码。
  • 在特定于Windows的代码中,总是调用函数的“W”版本,根据需要在UTF-8和UTF-16之间转换字符串参数。

这也是波科采取的方法 。

这真的是平台的依赖,Unicode是头痛的问题。 取决于你使用的编译器。 对于MS(VS2010或更早版本)的老版本,您需要使用MSDN中描述的API

为VS2015

 std::string _old = u8"D:\\Folder\\This \xe2\x80\x93 by ABC.txt"s; 

根据他们的文档。 我无法检查那一个。

对于mingw,gcc等

 std::string _old = u8"D:\\Folder\\This \xe2\x80\x93 by ABC.txt"; std::cout << _old.data(); 

输出包含正确的文件名称…