Windows 程序支持 Unicode

摘要:
类UNIX系统默认支持UTF-8编码的Unicode字符串,标准库函数也默认支持UTF-8字符串,例如fopen。CopyFileW;该函数在Boost in Wide中实现,我们包括Widen和Narrow两种类型的函数的实现,并打包标准库函数,以便我们可以编写跨平台支持Unicode的程序。UTF-8 Everywhere中还提到,可以使用Windows的MultiByteToWideChar和WideCharToMultiByteAPI实现两个转换函数。View Boost Noside的控制台I/O实现使用ReadConsoleW/WriteConsoleW系统API。WriteConsoleW支持Unicode字符串的输出。相反,使用功能控制台正确显示中文字符。

宽字符

阅读了 UTF-8 Everywhere 一文,推荐在程序中对于字符串都使用 UTF-8 编码。Unix-like 系统默认是支持 UTF-8 编码的Unicode字符串,标准库函数也默认支持 UTF-8 字符串,如 fopen 等。但在 Windows 系统,由于历史原因,其对需要输入宽字符的函数提供了另外以 w 开头的标准库扩展函数,如 _wfopen 等。况且对标准库的 wchar_t 两种系统实现不一样,在 unix-like 系统中是占4字节的 UTF-8 编码,而在 Windows 系统中是占2字节的 UTF-16 编码。Windows 很多系统 API 接受 wchar_t 类型的字符串,这就需要把 UTF-8 编码的字符串转换为 UTF-16。

编码转换

UTF-8 Everywhere 文中提供了一个解决方案,在程序中的字符串统一使用 UTF-8 编码并使用 char 或 string 存储而不使用宽字符类型。在需要传入宽字符类型时进行转换,实现 widennarrow 两种类型的函数,完成 UTF-8 和 UTF-16 的互相转换。

std::string narrow(const wchar_t *s);
std::wstring widen(const char *s);
std::string narrow(const std::wstring &s);
std::wstring widen(const std::string &s);

wchar_t *widen(const char *s);
char *narrow(const wchar_t *s);

在调用需要传入宽字符串的 Windows API时,使用 widen 函数转换字符串。

CopyFileW(widen(existing_file).c_str(), 
            widen(new_file).c_str(),
            TRUE);

函数实现

Boost.Nowide 中,包含 widennarrow 两种类型函数的实现,并对标准库函数进行了包装,使得可以编写跨平台支持 Unicode 的程序。

UTF-8 Everywhere 中也提到可以使用 Windows 的 MultiByteToWideCharWideCharToMultiByte 两个 API 实现两个转换函数。

#include <windows.h>

wchar_t *widen(const char *s, wchar_t *ws, size_t ws_size) {
	size_t required_size;

	// Get the required buffer size
	required_size = MultiByteToWideChar(CP_UTF8, 0, s, -1, ws, 0);

	if (required_size >= ws_size)
		return NULL;

	// Convert NULL terminated UTF-8 string to the UTF-16 (wide character) string
	if (MultiByteToWideChar(CP_UTF8, 0, s, -1, ws, ws_size) == 0)
		return NULL;

	return ws;
}

char *narrow(const wchar_t *ws, char *s,  size_t s_size) {
	size_t required_size;

	// Get the required buffer size
	required_size = WideCharToMultiByte(CP_UTF8, 0, ws, -1, s, 0, NULL, NULL);

	if (required_size >= s_size)
		return NULL;

	// Convert NULL terminated UTF-8 string to the UTF-16 (wide character) string
	if (WideCharToMultiByte(CP_UTF8, 0, ws, -1, s, s_size, NULL, NULL) == 0)
		return NULL;

	return s;
}

写代码测试两个函数时,遇到了控制台输出乱码问题。UTF-8 字符串转换为 wchar_t 类型字符串之后应该就能使用 wprintf 函数输出,但实际只有英文字符能正常输出,中文就是乱码。这主要时因为控制台使用的编码方式不是 Unicode, 中文的系统默认是 GBK,而宽字符输出的是 UTF-16,这中间就存在编码转换的问题,库函数 wprintf 没有自动转换。查看 Boost.Nowide 对 Console I/O 的实现说明,其利用的是 ReadConsoleW/WriteConsoleW 系统 API。WriteConsoleW 支持输出 Unicode 字符串,改用该函数控制台正确显示中文字符。

void print_wstring(const wchar_t *ws) {
	DWORD w;
	WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE), ws, wcslen(ws), &w, NULL);
}

int main(void) {
	char cc[] = "xE4xB8x80y
";
	wchar_t utf16[512];
	wchar_t uu[] = L"一
";
	print_wstring(widen(cc, utf16, sizeof(utf16)));
	print_wstring(uu);
	system("pause");
	return 0;
}

Windows 程序支持 Unicode第1张

源代码中的 UTF-8 字符串非 ASCII 字符直接使用16进制表示,wchar_t 类型的可以直接输入,但源代码文件使用的编码方式要支持Unicode 编码。编译器会自动根据源代码文件的编码方式解码字符串并使用 wchar_t 类型的编码方式编码字符串存储在最终编译生成的可执行文件中,在 Windows 系统中就是 UTF-16。为了避免不必要的编码问题,源代码文件也统一使用 UTF-8 编码保存,不过 visual studio 要使用带 BOM 的 UTF-8,不带 BOM 的不能正确识别。vs 2010 中打开 File -> Adavanced Save Options进行设置。

免责声明:文章转载自《Windows 程序支持 Unicode》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇线段树详解(原理、实现与应用)IO流与NIO流下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Java读取本地文件进行unicode解码

工具使用: package test.opservice; import eh.util.MapValueUtil; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; /** * Cre...

Unicode和ANSI之间转换 NotePad++轻松实现

  今天碰到一个关于UNICODE和ANSI相互转换的问题,作为一个程序员,我首先想到的是用WideCharToMultiByte/MultiByteToWideChar实现,这个我想大家都会了,我也会在后续博客中介绍如何使用。本博客主要是介绍如何通过NotePad++工具来实现的,NotePad++功能比NotePad强大很多,这我也不赘述了。转入正题吧...

Python字符编码

字符编码 计算机只认识数字,我们平时在使用计算机时,用的都是人类能读懂的字符(用高级语言编程的结果也无非是在文件内写了一堆字符),如何能让计算机读懂人类的字符?必须经过一个过程: 字符--------(翻译过程)------->数字 这个过程实际就是一个字符如何对应一个特定数字的标准,这个标准称之为字符编码 一、存取文件的原理(nodepad++,...

[转载]Unicode中对中文字符的编码

以前写过一篇贴子是写中文在unicode中的编码范围 unicode中文范围,但写的不是很详细,今天再次研究了下unicode,并给出详细的unicode取值范围。 本次研究的unicode对象是unicode 5.2.0版本。现在最新的是6.0版 对于这次研究的unicode把编码分为以下几个平面(英文中是plane,可以认为就是不同的区位) Unico...

.net读取Windows登录用户信息(downmoon)

前天,在CodeProject看到一篇文章http://www.codeproject.com/KB/system/LSAEnumUserSessions.aspx 如何读取windows 当前登录用户的状态信息。 主要代码分享如下: 一:导入dll Code /**//******************************************...

脚本中的无效字符

脚本被保存后 就无法解析 报无效字符 是编码的问题  用ue 的16进制查看 发现 开头多了EFBBBF  (本身是没有的 ) 选 另存为 utf—8 无 bom utf—8 是一种unicode 用ansi虽然也没有efbbbf但是 里面的 汉字编码就不对了 ...