UNICODE, UTF-8 and Windows mess

UNICODE, UTF-8 and Windows mess

本文关键字:Windows mess and UTF-8 UNICODE      更新时间:2023-10-16

我正在尝试在Windows中实现文本支持,目的是以后还搬到Linux平台。以统一的方式支持国际语言是理想的选择,但是考虑到这两个平台时,这似乎并不容易完成。我花了大量时间阅读Unicode,UTF-8(和其他编码),Widechars等,这是我到目前为止所了解的:

Unicode作为标准,描述了可映射的字符集和发生的顺序。我将其称为"什么":Unicode指定将有什么可用。

utf-8(和其他编码)指定:如何以二进制格式表示每个字符。

现在,在Windows上,他们最初选择了UCS-2编码,但未能满足要求,因此UTF-16是他们拥有的,在必要时也是多ch的。

所以这是三人:

  1. Windows仅在内部使用UTF-16,因此,如果您想支持国际角色,则被迫将其转换为Widechar版本以相应地使用OS调用。似乎没有任何支持使用多字节UTF-8字符串来调用CreateFilea()之类的东西,并且看起来很合适。这是正确的吗?
  2. 在C中,有一些多字节支持函数(_mbscat,_mbscpy等),但是,在Windows上,字符类型定义为这些函数的无符号字符*。鉴于_MBS系列函数不是一个完整的集合(即没有_mbstol可以将多字节字符串转换为长的_mbstol,例如,您被迫使用运行时函数的某些char*版本,由于这些功能之间的签名/未签名类型差异,这导致了编译器问题。有人使用这些吗?您只是做一大堆铸造以解决错误吗?
  3. 在C 中,STD :: String具有迭代器,但它们基于Char_Type,而不是代码点。因此,如果我在std :: string :: iterator上做A ,我将获得下一个char_type,而不是下一个代码点。同样,如果您调用std :: string :: operator [],您将获得对char_type的引用,该char_type具有很大的潜力,即不成为完整的代码点。那么,如何按代码点迭代std ::字符串呢?(C具有_mbsinc()函数)。

只是做UTF-8

在每个细胞中,UTF-8都有很多支持库,有些也是多平流。正如您已经指出的那样,Win32中的UTF-16 API有限且不一致,因此最好将所有内容保留在UTF-8中,并终于转换为UTF-16。Windows API也有一些方便的UTF-8包装。

此外,在应用程序级文档中,UTF-8越来越被标准化。每个文本处理应用程序要么接受UTF-8,要么在最坏的情况下显示为" ASCII,具有一些dingbats",而只有很少的应用程序支持UTF-16文档,而那些不支持它的文档,将其显示为"很多whitespace!

  • 正确。您将以Windows API调用将UTF-8转换为UTF-16。

  • 大多数时候,您将使用常规字符串函数用于UTF-8-strlenstrcpy(ICK),snprintfstrtol。它们将使用UTF-8字符正常工作。要么将char *用于UTF-8,要么您必须施放所有内容。

    请注意,诸如_mbstowcs之类的下划线版本不是标准的,通常没有下划线,例如mbstowcs

  • 很难想到您实际上想在Unicode字符串上使用operator[]的示例,我的建议是远离它。同样,在字符串上进行迭代的用途很少:

    • 如果您要解析字符串(例如,字符串是C或JavaScript代码,也许您需要语法Hilighting),那么您可以完成大部分的工作字节字节,而忽略了多重型方面。

    • 如果您正在进行搜索,则还将执行此字节字节(但请记住要首先归一化)。

    • 如果您正在寻找单词断开或素数集群边界,则需要使用像ICU这样的库。该算法并不简单。

    • 最后,您始终可以将一块文本转换为UTF-32并以这种方式使用。我认为,如果您要实现任何Unicode算法(例如Compration或Breaking)。

    请参阅:C 迭代或将UTF-8字符串分为符号数组?

    1. Windows仅在内部使用UTF-16,因此,如果您想支持国际角色,则被迫将其转换为Widechar版本以相应地使用OS调用。似乎没有任何支持使用多字节UTF-8字符串来调用CreateFilea()之类的东西,并且看起来很合适。这是正确的吗?

    是的,这是正确的。*A函数变体根据当前活动的代码页面解释字符串参数(在美国和西欧的大多数计算机上是Windows-1252,但通常可以是其他代码页),并将其转换为UTF-16。有一个UTF-8代码页面,但是AFAIK没有办法可以编程设置活动代码页(有GetACP获取活动代码页,但没有对应的SetACP)。

    1. 在C中,有一些多字节支持函数(_mbscat,_mbscpy等),但是,在Windows上,字符类型定义为这些函数的无符号字符*。鉴于_MBS系列函数不是一个完整的集合(即没有_mbstol可以将多字节字符串转换为长的_mbstol,例如,您被迫使用运行时函数的某些char*版本,由于这些功能之间的签名/未签名类型差异,这导致了编译器问题。有人使用这些吗?您只是做一大堆铸造以解决错误吗?

    根据我的经验,mbs*功能系列几乎从未使用过。除mbstowcsmbsrtowcsmbsinit外,这些功能不是标准c。

    1. 在C 中,STD :: String具有迭代器,但它们基于Char_Type,而不是代码点。因此,如果我在std :: string :: iterator上做A ,我将获得下一个char_type,而不是下一个代码点。同样,如果您调用std :: string :: operator [],您将获得对char_type的引用,该char_type具有很大的潜力,即不成为完整的代码点。那么,如何按代码点迭代std ::字符串呢?(C具有_mbsinc()函数)。

    我认为mbrtowc(3)将是解码多键字符串的单个代码点的最佳选择。

    总的来说,我认为跨平台Unicode兼容性的最佳策略是使用单字节字符在内部完成UTF-8的所有操作。当您需要调用Windows API函数时,将其转换为UTF-16,并始终调用*W变体。大多数非Windows平台已经使用UTF-8,因此可以将其捕捉。

    在Windows中,您可以调用WideCharToMultiByteMultiByteToWideChar之间转换UTF-8字符串和UTF-16字符串(Windows中的WSTRING)。由于Windows API不使用UTF-8,因此每当您调用任何支持Unicode的Windows API函数时,都必须将字符串转换为WSTRING(UTF-16中的Unicode的Windows版本)。当您从Windows获得输出时,必须将UTF-16转换回UTF-8。Linux在内部使用UTF-8,因此您不需要这种转换。要使您的代码移植到Linux,请坚持使用UTF-8,并提供以下转换的内容:

    #if (UNDERLYING_OS==OS_WINDOWS)
     
    using os_string = std::wstring;
    std::string utf8_string_from_os_string(const os_string &os_str)
    {
        size_t length = os_str.size();
        int size_needed = WideCharToMultiByte(CP_UTF8, 0, os_str, length, NULL, 0, NULL, NULL);
        std::string strTo(size_needed, 0);
        WideCharToMultiByte(CP_UTF8, 0, os_str, length, &strTo[0], size_needed, NULL, NULL);
        return strTo;
    }
    os_string utf8_string_to_os_string(const std::string &str)
    {
        size_t length = os_str.size();
        int size_needed = MultiByteToWideChar(CP_UTF8, 0, str, length, NULL, 0);
        os_string wstrTo(size_needed, 0);
        MultiByteToWideChar(CP_UTF8, 0, str, length, &wstrTo[0], size_needed);
        return wstrTo;
    }
    #else
    // Other operating system uses UTF-8 directly and such conversion is
    // not required
    using os_string = std::string;
    #define utf8_string_from_os_string(str)    str
    #define utf8_string_to_os_string(str)    str
    #endif
    

    要迭代UTF8字符串,您需要的两个基本函数是:一个用于计算UTF8字符的字节数,而另一个可以确定字节是否是UTF8字符序列的领导字节。以下代码提供了一种非常有效的测试方法:

    inline size_t utf8CharBytes(char leading_ch)
    {
        return (leading_ch & 0x80)==0 ? 1 : clz(~(uint32_t(uint8_t(leading_ch))<<24));
    }
    inline bool isUtf8LeadingByte(char ch)
    {
        return  (ch & 0xC0) != 0x80;
    }
    

    使用这些功能,不难在UTF8字符串上实现自己的迭代器,一个是用于转发迭代器,另一个用于落后迭代器。