如何让STL std :: string在Windows上使用unicode?

时间:2010-07-15 15:43:07

标签: c++ windows string unicode stl

在我的公司,我们有一个跨平台(Linux和Windows)库,它包含我们自己的STL std :: string扩展,这个类在字符串之上提供所有类型的功能;最近我们得到了使这个字符串unicode“友好”的要求,基本上它需要支持中文,日文,阿拉伯文等字符。经过初步研究,这似乎在Linux方面很好因为每件事本身就是UTF-8,但是我在Windows方面遇到了麻烦;是否有一个技巧让STL std :: string在Windows上作为UTF-8工作?它甚至可能吗?有没有更好的办法?理想情况下,我们会基于std :: string保留自己,因为这是Linux中基于字符串类的内容。

谢谢,

9 个答案:

答案 0 :(得分:12)

你的问题有几个误解。

  • C ++和STL都没有处理编码。

  • std::string本质上是 bytes 的字符串,而不是字符。所以你应该没有问题填充UTF-8编码的Unicode。但是,请记住,所有string函数也适用于字节,因此myString.length()将为您提供字节数,而不是字符数。

  • Linux 本身就是UTF-8。现在大多数发行版默认为UTF-8,但不应该依赖它。

答案 1 :(得分: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。 / p>

许多这些问题源于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”文本字符串是“apple”。一个char数组,不知道它是什么编码,是一个灾难的处方。

答案 2 :(得分:7)

无论平台如何,将UTF-8代码点放入std::string都应该没问题。 Windows上的问题是几乎没有其他任何期望或与UTF-8一起工作 - 它期望并与UTF-16一起使用。您可以切换到存储UTF-16的std::wstring(至少在大多数Windows编译器上),或者您可以编写其他接受UTF-8的例程(可能转换为UTF-16,然后传递给OS)。

答案 3 :(得分:4)

你看过std::wstring了吗?它是std::basic_string的{​​{1}}版本,而不是wchar_t使用的char版本。

答案 4 :(得分:2)

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

在这种情况下,这对我来说最有效(具有Windows和Linux版本的跨平台应用程序)。

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

我尝试但不太喜欢的其他方法:

  • typedef std::basic_string<TCHAR> tstring;然后在业务代码中使用tstring。可以使用Wrappers /重载来简化std :: string和std :: tstring之间的转换,但它仍然会增加很多痛苦。
  • 到处使用std::wstring。由于wchar_t在Windows上为16位,所以没有多大帮助,因此您要么必须限制自己使用BMP,要么需要进行大量复杂操作才能使代码处理Unicode跨平台。在后一种情况下,超过UTF-8的所有好处都会消失。
  • 在平台特定部分使用ATL / WTL / MFC CString;在跨平台部分使用std::string。这实际上是我推荐的变体。 CString在许多方面优于std::string(在我看来)。但它引入了额外的依赖性,因此并不总是可接受或方便。

答案 5 :(得分:2)

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

答案 6 :(得分:1)

在Windows API和C运行时库中,char*参数被解释为在“ANSI”代码页中进行编码。问题是UTF-8 isn't supported as an ANSI code pageI find incredibly annoying

我处于类似的情况,正在将软件从Windows移植到Linux,同时也使其具有Unicode感知能力。我们为此采取的方法是:

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

这也是the approach Poco has taken

答案 7 :(得分:0)

它真的依赖于平台,Unicode很头疼。取决于您使用的编译器。对于MS(VS2010或更早版本)的旧版本,您需要使用MSDN中描述的API

for 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();

输出包含正确的文件名...

答案 8 :(得分:0)

你应该考虑使用 QString 和 QByteArray,它有很好的 unicode 支持