我可以依靠TCHAR的定义对我使用的字符集做出正确的假设吗?

时间:2019-07-13 09:51:01

标签: c++ winapi mfc

我正在重新研究一个旧的MFC业余项目,并试图使其对Unicode更友好。因此,我一直将char的所有实例替换为TCHAR,将strlen()替换为_tcslen(),依此类推。

但是,I just discovered实际上这些类型和功能与所有语言字符集不兼容。例如,日语字符显然由三个字节而不是一个字节表示:

  

我想知道TCHAR数组或TCHAR *中的字符数。不幸的是,我可以找到的每个长度函数(_tcslen(),甚至wstring :: length())似乎都返回了BYTES的数量,而不是字符...日语字符算作3,而罗马字符算作1。 / p>

但是,this Microsoft documentation page建议使用TCHAR可以在所有情况下确保您的安全:

  

为了在所有情况下都安全,在处理TCHAR时应使用以下约定:

TCHAR tchBuffer[24];
GetWindowText( hWnd, tchBuffer, sizeof(tchBuffer)/sizeof(TCHAR));
     

这样做,将您的代码编译为MBCS或UNICODE时将是安全的。

这是真的吗?还是将诸如日文这样的多字节字符集生成为UB? MTIA:-)

3 个答案:

答案 0 :(得分:2)

您确实需要确定主要的api是您的应用程序目标。

如果已将其标记为基于MFC的,则应使用MFC的c ++字符串表示形式CString及其在Windows平台上处理Ansi和Unicode的规则。

同样,如果您主要是针对Windows API编写的,则定义的类型为:CHAR,TCHAR和WCHAR用于字符文字,而* STR,* TSTR和* WSTR用于字符串缓冲区。

如果您是首先编写一个c ++应用程序-碰巧是在Windows上实现的-那么则更喜欢std:类型,例如std:string和std:wstring

最后,如果要通过字符串的C表示形式表示字符串,则可以使用char *,wchar_t *表示,如果希望能够在unicode和ansi之间动态切换,则可以使用_tchar *及其在.in中定义的帮助程序类型。 / p>

在Ansi和Uniocode之间切换 在所有类型中,当在Ansi和Unicode之间切换编译器时,CString,TCHAR,* TSTR和_tchar将在8位和16位类型之间切换。

但实际上-将应用程序编译为Ansi: *由于Windows API一直是nativley unicode已有一段时间了,因此效率低下,因此Ansi应用程序中所有具有字符串参数的api调用都必须在进出时将所有in参数转换为出入参数。 *易于丢失数据,因为Ansi应用程序永远(几乎)永远无法同时使用来自两个不同代码页的字符。

Ansi / MBCS可以安全地编码什么 Windows API定义了“ Ansi代码页”。我不知道为什么叫Ansi,但是您可以通过调用GetACP来获得当前的Ansi。如果将其设置为例如CP_LATIN1,然后尝试加载,处理,输入或处理日语,韩语等字符将失败。这是区域设置控制面板中的系统范围默认设置,因此通常您应该为本地用户使用正确的代码页。

如果您正在使用c运行时函数,则需要调用setlocale以确保 it 知道您正在使用哪种编码。我不确定std :: string是否使用c语言环境,或者是否存在此想法的std ::抽象。关键是,要知道您主要使用的是哪种字符串抽象,并使用它,因此您不必仅对某些不同的本地或代码页面api进行调用就可以对代码进行修饰再次弹出一个字符串。

上手:Utf8 另一方面,其他行业则朝着另一个方向发展,Linux,MacO和相应的大多数跨平台库使用Utf8编码处理unicode字符。可以对所有可能的unicode字符进行编码,而不会与语言环境或代码页或任何废话混为一谈。以及所有具有跨平台友好性的“ char *”。 因此,如果编写跨平台代码对您很重要,那么您将不会使用wchar_t或任何宽字符类型。 Windows 10最终将Utf8添加为可能的Ansi代码页BUT:这是用户必须选择加入的系统设置,因此您的应用程序无法声明或依赖于启用它。我不知道是否可以简单地将其设置为当前线程代码页,我也不知道是否有任何c运行时兼容/利用此优势提供无缝的“更接近posix”体验您可以期望字符串可以正常工作。

当然,这里需要说明的是,“字符”现在可以被编码为1到6个字节长。

字节长度与字符数 不确定在这里想要什么。通常,您不希望* strlen之类的函数返回字符数,因为(通常)要使用它们的结果来分配内存缓冲区。但是,它们应该返回的计数不是以字节为单位,而是以您要处理的字符的自然分配单位为单位。即wcslen(“ hello”)应该返回5,而不管wchar_t的宽度如何,该宽度可以为2或4个字节。

wchar_t wchar_t是一种可怕的类型,因为c / c ++标准未定义其宽度。一些编译器以2字节为单位,其他为4字节。作为2字节单元,它仅足够宽以存储来自Unicode“ BMP”或基本多语言平面的字符,但是有些字符不能存储为单个UCS2 / UTF-16字符。如果要100%安全,则必须使用char16_t,char32_t或您特别需要的任何东西。 wchar_t不是安全的选项。

总的说来,这是完全可恨的:

  • 您不能在所有地方使用普通的旧字符,而将utf-8用作明智的默认设置,因为Windows是Utf-16本机的,并且使用8位字符集的效率非常低,
  • 并且永远不能保证您会期望使用utf-8,因此您很可能会随机接受有损编码。
  • 您不能在任何地方都使用wchar_t,因为它在不同平台上的大小不同。
  • 如果您可以访问稳定的Utf-16:-posix平台将带有utf8的普通旧char *缓冲区用于所有内容,从而导致这些平台上的性能反比问题,那么您仍然必须处理理论上的多单元特性。
  • 使用TCHAR / _tchar类型并利用Visual Studios的Unicode /多字节字符集开关进行编译是无法忍受的,因为它给您的应用程序增加了很多额外的噪音,并且对于所有_t *而言,并不能真正帮助跨平台可移植性**函数仅是ms c-runtime的一部分。

答案 1 :(得分:0)

如评论中所述,使用wchar_t会产生更好的结果。

MFC是在通常使用char的时候设计的,而多字节字符集只能编码一种语言(例如Shift-JIS是日语字符的编码)。

从那时起,wchar_t已被用作可用集(在Windows上,wchar_t是无符号的缩写,并且编码UTF-16)。

我的建议是直接转换为wchar_t,而忽略tchar中间位置。

UTF-16确实对一些具有多个int16值的字符进行了编码

答案 2 :(得分:0)

  

这样做,将您的代码编译为MBCS或UNICODE时将是安全的。

这是不正确的,无论您使用的是哪种基本字符类型。

以任意/缓冲区大小的偏移量剪切未知字符串永远都不安全。 UTF-16(在Windows平台上为wchar_t)具有guide,即使您切换到UTF-32,您仍然会遇到surrogate pairsdecomposed combining characters和颜色修饰符的问题。 / p>

使用GetStringType获取有关特定字符的信息和/或使用CharNext遍历字符串以找到某个合适的停止点。