[英]What unicode encoding (UTF-8, UTF-16, other) does Windows use for its Unicode data types?
[英]UNICODE, UTF-8 and Windows mess
我正在尝试在 Windows 中实现文本支持,目的是稍后也迁移到 Linux 平台。 以统一的方式支持国际语言是最理想的,但考虑到所讨论的两个平台,这似乎并不容易实现。 我花了相当多的时间阅读 UNICODE、UTF-8(和其他编码)、widechars 等,这是我到目前为止所了解的内容:
作为标准,UNICODE 描述了可映射的字符集及其出现的顺序。 我将此称为“什么”:UNICODE 指定可用的内容。
UTF-8(和其他编码)指定如何:每个字符将如何以二进制格式表示。
现在,在windows上,他们本来选择了UCS-2编码,但是不能满足要求,所以他们有UTF-16,必要时也是多字符。
所以这里是困境:
只做UTF-8
在每个plaftorm中都有很多UTF-8的支持库,也有一些是多平台的。 Win32中的UTF-16 API是有限的,并且与您已经注意到的不一致,因此最好将所有内容保存在UTF-8中并在最后时刻转换为UTF-16。 Windows API还有一些方便的UTF-8包装。
此外,在应用程序级文档中,UTF-8作为标准越来越被接受。 每个文本处理应用程序都接受UTF-8,或者最坏的情况下将其显示为“带有一些dingbats的ASCII”,而只有少数应用程序支持UTF-16文档,而那些不支持UTF-16文档的应用程序则显示为“批量和批量”空白!“
正确。 您将为Windows API调用将UTF-8转换为UTF-16。
大多数情况下,您将使用UTF-8的常规字符串函数 - strlen
, strcpy
(ick), snprintf
, strtol
。 它们可以与UTF-8字符一起使用。 要么使用char *
作为UTF-8,要么你必须投射一切。
请注意,像_mbstowcs
这样的下划线版本不是标准版本,它们通常以没有下划线的名称命名,例如mbstowcs
。
很难想出你真正想在Unicode字符串上使用operator[]
例子,我的建议是远离它。 同样,迭代字符串的用法令人惊讶:
如果要解析字符串(例如,字符串是C或JavaScript代码,也许您需要语法高亮显示),那么您可以逐字节地完成大部分工作并忽略多字节方面。
如果您正在进行搜索,您也将逐字节地执行此操作(但请记住首先进行规范化)。
如果您正在寻找单词中断或字形集合边界,您将需要使用像ICU这样的库。 算法并不简单。
最后,您始终可以将一大块文本转换为UTF-32并以此方式使用它。 我认为如果您正在实施任何Unicode算法(如整理或分解),这是最常用的选项。
- Windows内部仅执行UTF-16,因此如果要支持国际字符,则必须转换为其widechar版本以相应地使用OS调用。 似乎没有任何支持使用多字节UTF-8字符串调用类似CreateFileA()的东西并让它看起来正确。 它是否正确?
对,那是正确的。 *A
函数变体根据当前活动的代码页(在美国和西欧的大多数计算机上是Windows-1252,但通常可以是其他代码页)来解释字符串参数,并将它们转换为UTF-16。 有一个UTF-8代码页,但是AFAIK没有办法以编程方式设置活动代码页(有GetACP
来获取活动代码页,但没有相应的SetACP
)。
- 在C中,有一些多字节支持函数(_mbscat,_mbscpy等),但是,在Windows上,字符类型被定义为这些函数的unsigned char *。 鉴于_mbs系列函数不是一个完整的集合(例如,没有_mbstol将多字节字符串转换为long,例如),您将被迫使用运行时函数的一些char *版本,由于这些函数之间的有符号/无符号类型差异,这会导致编译器问题。 有没有人甚至使用那些? 你只是做了一大堆铸造来解决错误吗?
根据我的经验, mbs*
系列功能几乎从未使用过。 用的例外mbstowcs
, mbsrtowcs
,和mbsinit
,这些功能都没有标准C.
- 在C ++中,std :: string有迭代器,但它们基于char_type而不是代码点。 因此,如果我在std :: string :: iterator上执行++,我会得到下一个char_type,而不是下一个代码点。 类似地,如果你调用std :: string :: operator [],你会得到一个char_type的引用,它很有可能不是一个完整的代码点。 那么如何通过代码点迭代std :: string呢? (C具有_mbsinc()函数)。
我认为mbrtowc(3)
将是解码多字节字符串的单个代码点的最佳选择。
总的来说,我认为跨平台Unicode兼容性的最佳策略是使用单字节字符在内部执行UTF-8中的所有操作。 当您需要调用Windows API函数时,将其转换为UTF-16并始终调用*W
变体。 大多数非Windows平台已经使用UTF-8,因此可以快速使用它们。
在 Windows 中,可以调用WideCharToMultiByte
和MultiByteToWideChar
在 UTF-8 字符串和 UTF-16 字符串(Windows 中为 wstring)之间进行转换。 因为 Windows API 不使用 UTF-8,所以每当你调用任何支持 Unicode 的 Windows API function 时,你必须将字符串转换为 wstring8 in 848TF18 中的 848-18 版本(Windows U8-65 版本) 当你从 Windows 得到 output 时,你必须将 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字符串上实现自己的迭代器应该不难,一个是forwarding iterator,一个是backward iterator。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.