我可以依靠 TCHAR 的定义对我正在使用的字符集做出正确的假设吗?
Can I rely on TCHAR's definition to make the correct assumptions about the charset I'm using?
我正在重温一个旧的MFC爱好项目,并试图使其对Unicode更加友好。因此,我一直在用TCHAR
替换所有char
实例,strlen()
用_tcslen()
等等。
但是,我刚刚发现这些类型和函数实际上并不与所有语言字符集兼容。例如,日语字符显然由三个字节表示,而不是一个:
我想知道 TCHAR 数组或 TCHAR* 中的字符数。不幸的是,我能找到的每个长度函数(_tcslen(),甚至 wstring::length()) 似乎都在返回字节数,而不是字符数......日语字符计为三个,罗马字符计为一个。
但是,此Microsoft文档页面建议使用TCHAR
s 将确保您在所有情况下的安全:
为了在所有情况下都安全起见,在处理 TCHAR 时应使用以下约定:
TCHAR tchBuffer[24]; GetWindowText( hWnd, tchBuffer, sizeof(tchBuffer)/sizeof(TCHAR));
这样,您的代码在编译为 MBCS 或 UNICODE 时将是安全的。
这是真的吗?或者多字节字符集(如日语字符集)会导致 UB?MTIA :-)
您确实需要确定应用程序面向的主要 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* 及其在 中定义的帮助程序类型。
在 Ansi 和 Uniocode之间切换 在所有类型中,当您在 Ansi 和 Unicode 之间切换编译器时,CString、TCHAR、*TSTR 和 _tchar 将在 8 位和 16 位类型之间切换。
但实际上 - 将应用程序编译为 Ansi: * 效率低下,因为 Windows API 已经使用了一段时间的原生 Unicode,因此 Ansi 应用程序中具有字符串参数的所有 API 调用都被强制在进出时转换其所有输入参数,并在输出时转换参数。 * 容易丢失数据,因为 Ansi 应用程序(几乎)永远不会同时处理来自两个不同代码页的字符。
无论如何,Ansi/MBCS 可以安全地编码什么Windows API 定义了一个"Ansi 代码页"。我不知道为什么它叫Ansi,但是你可以通过打电话GetACP
来获得当前的.如果设置为例如CP_LATIN1,则尝试加载、处理、输入或处理日语、韩语等字符将失败。这是区域设置控制面板中的系统范围默认设置,因此通常应具有本地用户的正确代码页。
如果您使用的是 c 运行时函数,则需要调用setlocale
以确保它知道您正在使用的编码。我不确定 std::string 是否使用 c 语言环境,或者是否有这个想法的 std:: 抽象。关键是,要知道你主要使用哪个字符串抽象,并使用它,这样你就不必仅仅因为一些血腥的?而调用所有不同的本地/代码页 apis。s 或块再次在字符串中弹出。
另一方面:Utf8另一方面,该行业的其他公司已经朝着另一个方向发展,Linux,MacOS和相应的大多数跨平台库都使用Utf8编码处理Unicode字符。它对所有可能的 unicode 字符进行编码,而不会弄乱语言环境或代码页或任何废话。所有这些都带有非常跨平台友好的"char*"。 因此,如果编写跨平台代码对您很重要,那么您将不会使用wchar_t或任何宽字符类型。 Windows 10 最终将 Utf8 添加为可能的 Ansi 代码页,但是:它是用户必须选择加入的系统设置,因此您的应用程序无法声明或依赖它被启用。我不知道是否可以简单地将其设置为当前线程代码页,我也不知道是否有任何 c 运行时兼容/利用这一点来提供无缝的"更接近 posix"体验,您可以在其中期望字符串工作。
当然,这里需要注意的是,"字符"现在可以编码为1到6个字节。
字节长度与字符不确定你想要什么。您通常不希望像 *strlen 这样的函数返回字符数,因为您(通常)将使用它们的结果来分配内存缓冲区。但是,它们应该返回的计数不是以字节为单位,而是以您正在处理的字符的自然分配单位为单位。即 wcslen("hello") 应该返回 5,无论wchar_t的宽度如何,可以是 2 或 4 个字节。
wchar_twchar_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 的普通旧字符*缓冲区来处理导致这些平台上反向性能问题的所有内容,并且您仍然必须处理理论上的多单元特征。
- 使用 TCHAR/_tchar 类型并利用 Visual Studios 的 Unicode/Multibyte 字符集编译器开关是无法容忍的,因为它会为您的应用程序添加大量额外的噪音,并且并不能真正帮助跨平台可移植性,因为所有 _t*** 函数都只是 ms c 运行时的一部分。
如评论中所述,使用 wchar_t会产生更好的结果。
MFC是在通常使用char的时代设计的,多字节字符集只能编码一种语言(例如Shift-JIS是日语字符的编码)。
从那以后,wchar_t接管了一个可用的集合(在Windows上wchar_t是一个无符号的短,并编码UTF-16)。
我的建议是直接转换为wchar_t,并忽略 tchar 中间位置。
UTF-16 确实使用多个 int16 值对某些字符进行编码
这样做,您的代码在编译为 MBCS 或 UNICODE 时将是安全的。
无论您使用哪种基本字符类型,这都是不正确的。
以任意/缓冲区大小的偏移量切碎未知字符串从来都不安全。UTF-16(在Windows平台上wchar_t
)具有代理项对,即使您切换到UTF-32,您仍然会遇到分解组合字符,二合字母和颜色修饰符的问题。
使用GetStringType
获取有关特定字符的信息和/或使用CharNext
遍历字符串以找到一个合适的停止点。
- 为什么 GCC 不能假设 std::vector::size 在这个循环中不会改变?
- 这个方程 x + y = x & y + x |y 成立(假设 x, y > 0)?
- C++ CUDA:如何将字节表示为字符或字符集?
- 假设声明中某些上下文中需要的名称查找规则是什么
- 使用 Visual Studio C++ 在控制台中控制字体和字符集
- 围绕"?"使用的混淆,因为没有假设值
- 如果使用多字节字符集,为什么TCHAR值会更改
- 假设 a 是双倍的,2.0*a 比 2*a 快吗?
- 假设CPU的解码指令
- 跨平台 mySQL 与字符集不一致
- 如何使用C++中不是文字的变量在数字中显示单引号和两个引号?假设 6'2" 英尺
- 假设传递给 OpenGL 的结构数组的内存布局存在潜在错误
- 标记字符串,接受CPP中给定字符集之间的所有内容
- 假设相同的 lambda 表达式具有不同的类型是否安全?
- 本征是否假设混叠?
- 假设容器值将自动创建是不好的做法吗?
- 为什么 constexpr 假设我的方法就是 const
- 假设 C++11 中已知子级布局,重新插入基类是否安全
- 我可以依靠 TCHAR 的定义对我正在使用的字符集做出正确的假设吗?
- 对于C++实现的字符集,什么假设是安全的?