在UTF-8内部工作,然后仅在Windows中需要时转换为UTF-16,是否存在任何危险

Are there any dangers to working internally in UTF-8 and then converting to UTF-16 only when needed in Windows?

本文关键字:转换 危险 UTF-16 是否 存在 任何 工作 内部 UTF-8 然后 Windows      更新时间:2023-10-16

Visual studio试图坚持使用tchar,当使用UNICODE选项编译时,基本上会使用Windows和其他API的广泛版本。

那么,在应用程序内部使用UTF-8(这使C++STL的使用更容易,也使跨平台代码的可读性更强),然后只在需要使用任何操作系统API时转换为UTF-16,这有什么危险吗?

我特别询问的是为多个操作系统开发的问题-不使用UTF-8的Windows和其他像Mac这样的操作系统。

正如其他人所说,在内部使用UTF-8,然后在需要调用Windows函数时进行转换没有危险。

然而,请注意,如果您显示大量文本,每次转换的成本可能会变得昂贵得令人望而却步。(记住,您不仅需要转换,还可能需要分配和释放缓冲区来保存临时转换字符串。)

我还应该指出STL中内置了广泛的字符支持,所以真的没有理由这么做。(std::wstring等)

此外,仅使用UTF-8对英语来说是可以的,但如果您计划支持东欧、阿拉伯语或亚洲字符集,您对文本的存储要求可能会比UTF-16更大(因为需要存储三到四个代码点的字符更多)。同样,如果你要处理大量的文本,这可能只是一个问题,但这是一个需要考虑的问题——如果你要在任何时候通过网络连接传输这些文本,这就需要加倍考虑。

由于UTF-8和UTF-16只是编码数字的两种方式(然后被解释为所谓的代码点或字形),因此来回转换没有错:不会丢失任何信息。所以不,转换没有危险(当然,只要转换是正确的)。

如果您的操作系统在其API中使用wid(er)字符,并且您正在编写一个需要国际化支持的应用程序,那么在程序中使用char和UTF-8作为内部表示是完全愚蠢的。您正在反向使用UTF-8。UTF-8用于通过操作系统接口以及无法直接处理宽字符的存储和数据交换格式走私Unicode。

我假设您的项目不是关于文本处理、操作或转换的:对于文本处理,选择一种并且只有一种编码要容易得多,在所有平台上都是一样的,然后在使用本机API时根据需要进行转换。

但是,如果您的项目不是以文本处理/操作/转换为中心,那么在所有平台上对UTF-8的限制并不是最简单的解决方案。

避免在Windows上使用char

如果在Windows开发中使用char类型,那么所有WinAPI都将使用char

问题是,在Windows上的CCD_;"历史";应用程序,即unicode之前的应用程序。

每个char文本都被解释为非Unicode文本,其编码/字符集由Windows用户选择,而不是由开发人员选择。

意思是:如果你认为你使用的是UTF-8,那么将UTF-8 char文本发送到WinAPI,在GUI(和TextBox等)上输出,然后在阿拉伯语设置的Windows上执行你的代码(例如),那么你会看到你漂亮的UTF-8字符文本不会被WinAPI正确处理,因为Windows上的WinAPI认为所有char都将被解释为Windows-1256编码。

如果您在Windows上使用char,您将放弃Unicode,除非对WinAPI的每次调用都经过翻译(通常通过GTK+、QT等框架,但它可能是您自己的包装函数)。

优化是万恶之源,但在我看来,每次与Windows讨论时,将所有UTF-8文本从UTF-16转换为UTF-16似乎是一种无用的令人讨厌的做法。

备选方案:为什么不在所有平台上使用TCHAR

您应该使用TCHAR,为Linux/MacOS/Whatever提供一个类似于tchar.h的标头(重新定义原始tchar.h标头中的宏等),并为要使用的标准库对象提供一个tchar.h样的标头。例如,我自己的tstring.hpp类似于:

// tstring.hpp
#include <string>
#include <sstream>
#include <fstream>
#include <iostream>
#ifdef _MSC_VER
#include <tchar.h>
#include <windows.h>
#else
#ifdef __GNUC__
#include <MyProject/tchar_linux.h>
#endif // __GNUC__
#endif

namespace std
{
#ifdef _MSC_VER
   // On Windows, the exact type of TCHAR depends on the UNICODE and
   // _UNICODE macros. So the following is useful to complete the
   // tchar.h headers with the C++ Standard Library's symbols.
   #ifdef UNICODE
      typedef              wstring        tstring ;
      // etc.
      static wostream &    tcout          = wcout ;
   #else // #ifdef UNICODE
      typedef              string         tstring ;
      // etc.
      static ostream &     tcout          = cout ;
   #endif // #ifdef UNICODE
#else // #ifdef _MSC_VER
    #ifdef __GNUC__
    // On Linux, char is expected to be UTF-8 encoded, so the
    // following simply maps the txxxxx type into the xxxxx
    // type, forwaking the wxxxxx altogether.
    // Of course, your mileage will vary, but the basic idea is
    // there.
    typedef                string         tstring ;
    // etc.
    static ostream &       tcout          = cout ;
    #endif // __GNUC__
#endif // #ifdef _MSC_VER
} // namespace std

Discplaimer:我知道,在std中声明事情是邪恶的,但我在这个特定的主题上除了迂腐之外还有其他事情要做

使用这些头,您可以将C++标准库与TCHAR功能结合使用,也就是说,使用std::tstring,它将在Windows上编译为std::wstring(前提是您编译时定义了UNICODE_UNICODE定义),并在要支持的其他基于char的操作系统上编译为std::string

因此,您将能够免费使用平台的原生字符类型

只要您对TCHAR字符类型不可知,就不会有任何问题。

对于您真正想处理UTF-8与UTF-16的肮脏一面的情况,则需要提供转换代码(如果需要)等

这通常是通过为不同类型和每个操作系统提供相同功能的重载来实现的。这样,就可以在编译时选择正确的函数。

不,如果遵循指南,就没有危险[1]事实上,这是最明智和最简单的方法,[2]即使您只为Windows编写

请注意,对于欧洲语言和非BMP字符,UTF-8的长度永远不会超过UTF-16。它只为UTF-8中用3个字节编码的代码点和UTF-16中用2个字节编码,占用更多的空间,这正是U+0800到U+FFFF的范围,[3],它主要是CJK字符。

"危险"在于UTF-8字符数与ASCII字符数不同。例如,U+24B62是一个单一的Unicode字符,但可以扩展到4个UTF-8字节。(其他示例请参见此处。)

如果你不把两者互换使用,你会没事的。

UTF-8是一种狂野而古怪的字符表示方式。你应该尽可能避免使用它。windows API避免使用UTF-8。(如果你坚持使用"多字节"构建,而不是"unicode"构建,它会在隐蔽的情况下为你完成所有转换,这样它就可以继续使用UTF16,如果你不小心,所有这些隐藏转换的低效率会吞噬你。)wxWidgets库以同样的方式避免UTF-8,这是跨平台的MAC。

您应该从中得到一个提示,并避免自己使用UTF-8。

什么时候需要使用UTF-8?UTF16的问题在于它取决于硬件中实现的单词中的字节顺序。因此,当你在不同的计算机之间传输数据时,它们可能在硬件中使用不同的字节顺序,你必须使用在任何硬件上具有相同字节顺序的UTF8。这就是浏览器和WWW页面使用UTF8的原因。