如何最好地处理Windows';16位wchar_t丑陋?

如何最好地处理Windows';16位wchar_t丑陋?,c,windows,utf-8,mingw,utf-16,C,Windows,Utf 8,Mingw,Utf 16,我正在编写一个与mingw一起使用的包装层,它为应用程序提供了一个虚拟UTF-8环境。处理文件名的函数是包装器,它从UTF-8转换并调用相应的“_w”函数,依此类推。我遇到的最大问题是Windows的wchar\t是16位的 对于文件系统操作来说,这不是什么大问题。我可以在UTF-8和UTF-16之间来回转换,一切都能正常工作。但是标准的C多字节/宽字符转换API不允许多wchar\t字符 可能的解决办法: 提供CESU-8环境,而不是UTF-8环境。我真的不喜欢这个 简单地说,只支持BMP。将

我正在编写一个与mingw一起使用的包装层,它为应用程序提供了一个虚拟UTF-8环境。处理文件名的函数是包装器,它从UTF-8转换并调用相应的“_w”函数,依此类推。我遇到的最大问题是Windows的
wchar\t
是16位的

对于文件系统操作来说,这不是什么大问题。我可以在UTF-8和UTF-16之间来回转换,一切都能正常工作。但是标准的C多字节/宽字符转换API不允许多wchar\t字符

可能的解决办法:

  • 提供CESU-8环境,而不是UTF-8环境。我真的不喜欢这个
  • 简单地说,只支持BMP。将长度为4的UTF-8序列视为无效
  • 扩展包装器,用
    typedef int32\u t wchar\t替换mingw的
    wchar\t
    和处理
    WCHAR
    WCHAR\t
    不同。这是一个难题,但对于那些希望有一个干净的POSIX类型环境并且不使用
    wchar\t
    用于任何Windows API目的的应用程序来说,它可能是理想的移植
  • 以下是黑客攻击:
  • mbrtowc
    读取4字节UTF-8字符的前3个字节后,输出对应于高代理的
    wchar\u t
    ,并将剩余状态保留在
    mbstate\u t
    对象中。在接收到下一个字节时,它将其与保存的状态组合以输出低代理项。如果最后一个字节无效,它将返回-1(带有EILSEQ),并且输出流中会出现一个单独的代理项(bad…)

    wcrtomb
    在处理高代理项时输出UTF-8的前2个字节,并将剩余状态保存在其
    mbstate\u t
    对象中。当它随后处理低位代理时,它将其与保存的状态相结合,以输出UTF-8的最后2个字节。如果未接收到有效的低位代理,则返回-1(带EILSEQ),输出流中会出现不完整的UTF-8序列(bad…)

    这种攻击的好处是,只要输入有效,它就可以工作,并允许访问任何UTF-8字符,从而访问应用程序可能需要处理的任何可能的文件名/参数等文本

    缺点是它不严格符合ISO C(
    wchar\u t
    string不允许是有状态的),并且它延迟了对格式错误字符的检测,直到写入了不正确的部分输出


    我正在寻找关于不同选择的反馈,尤其是我提出的破解方案:是否合理,缺点是否可能导致严重错误,以及是否有任何其他缺点我还没有考虑过,这可能会使方案无法完全发挥作用。我也很高兴听到我没有想到的任何其他可能的解决方案。

    如果您在windows上,您可以使用MultiByteToWideChar和WideCharToMultiByte在UTF-16和UTF-8之间一次性转换整个字符串

    虽然GCC中的默认模式是32位WCHARGT,但编译开关会改变这种情况,更一般地说,C&C++规范不指定W查尔格T的大小,事实上W查尔格可以和char一样大小。 如果要避免使用Windows API(在Windows包装器代码中!?),请使用mbstowcs一次转换整个字符串。

    我会执行类似于#4的操作,但在确定输入有效之前不要生成任何输出

    • mbrtowc
      应解码整个字符。如果它在BMP之外,则输出高代理并将低代理存储在
      mbstate\u t
    • wcrtomb
      应在
      mbstate\u t
      中存储高位代理,然后输出所有4个UTF-8字节(如果字符有效)

    我想你误解了这个问题。包装器必须提供的一件事是C函数
    mbrtowc
    wcrtomb
    (以及其余的多字节/宽转换函数,原则上只调用这些基本函数),因为应用程序可能会使用它们将字符串作为字符进行处理。由于16位
    wchar\u t
    ,提供这些是很困难的。当然,提供一次可以处理整个字符串的任何函数都很容易。这听起来不错,但我不确定这是否可行。如果
    mbrtowc
    将低位代理项存储在
    mbstate\u t
    中,则它必须在下次调用时输出
    wchar\u t
    ,而不消耗任何输入。但0的返回值保留用于转换空字节/字符串结尾。我想它可能会从下一个字符中消耗一个额外的字节,但如果恰好是一个单字节字符,奇怪的缓冲将继续。你怎么看?这很难,因为C标准假定
    wchar\u t
    可以表示任何字符,而微软违反了这一标准。我认为不可能使用UTF-16编写一致的
    mbrtowc
    。如果调用代码检查
    wch
    的值,而不是根据返回值假设U+0000,则0返回值可能不是问题。我自己不使用
    mbrtowc
    (而是一个内部开发的转换函数,可以处理整个字符串),所以我不确定这在实践中会有多大问题。我肯定是用
    (l=mbrtowc(&wc,s,1,&state))>0作为循环条件编写代码的。我同意你所说的,这可能是不可能完全符合;这就是为什么我在寻找一种最不坏的非一致性方法。为下一个C标准建议的
    mbrtoc16
    函数应该返回
    (size\u t)(-3)
    “如果存储了前一个调用产生的下一个字符”。