宽字符

阅读了 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[] = "\xE4\xB8\x80y\n";
wchar_t utf16[512];
wchar_t uu[] = L"一\n";
print_wstring(widen(cc, utf16, sizeof(utf16)));
print_wstring(uu);
system("pause");
return 0;
}

源代码中的 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的更多相关文章

  1. windows控制台程序——关于UNICODE字符的总结(转)

    前言:从Windows NT/2000开如,Windows系统已经是一个标准的UNICODE系统,系统内部所有字符串存储及操作均使用UNICODE编码.因此Win32 API都是UNICODE版本的, ...

  2. C++不支持Unicode,即使utf8

    今天,字符串unicode我们已经不需要常理的理由,但是,一些有编程语言的悠久历史.这仍然是一个头疼. 尽管第三方库支持的假设,C++事实上没有真正有效地支持unicode.即使utf8.(注意:本文 ...

  3. Windows程序员必须知道的字符编码和字符集

     字符编码 (Character encoding) 在存储和传递文本过程中,为了使得所有电脑都能够正确的识别出文本内容,需要有一个统一的规则. 2. 字符集 (Character Set) ) 一般 ...

  4. VC6.0支持UNICODE的步骤

    针对MFC程序的开发,支持Unicode一共需要三步: Step1 设置->C/C++预处理定义中,删除_MBCS,添加_UNICODE,UNICODE. Step2 设置->Link-& ...

  5. 使你的C/C++代码支持Unicode(CRT字符串处理的所有API列表,甚至有WEOF字符存在)

    悉Microsoft支持Unicode的方式. 它的主要目的是方便你查询相关的数据类型和函数,以及修正相应的拼写错误. I18nGuy 主页 XenCraft (Unicode 咨询公司) Engli ...

  6. windows程序设计基础知识

    Win32 API(Application Programming Interface) Win32 API可认为是一个程序库,提供各式各样的与windows系统服务有关的函数. Win32 API是 ...

  7. Windows编程中UNICODE和_UNICODE定义问题

    Windows编程中UNICODE和_UNICODE定义问题 先转一篇文章: 初学Windows SDK编程时碰到过这个问题,相信很多初学Windows编程的人也都碰到过,后来慢慢搞明白些了,但有时别 ...

  8. Linux下运行windows程序

    现在Winxp停止了支持,那我们的windows程序是否可以再linux上执行呢,如下是一些参考的信息 在您的 Linux/Mac 操作系统上运行 Windows 软件 http://www.wine ...

  9. Windows程序----初识Windows程序

    先来看一些励志名言来激励一下自己吧!  励志名言:每一发奋发奋的背后,必有加倍的赏赐 1.有无目标是成功者与平庸者的根本差别. 2.成功不是将来才有的,而是从决定去做的那一刻起,持续累积而成. 3.当 ...

随机推荐

  1. axel

    Linux下多线程下载工具 - Axel 2011年10月8日 上午 | 作者:VPS侦探 Axel 是 Linux 下一个不错的HTTP/FTP高速下载工具.支持多线程下载.断点续传,且可以从多个地 ...

  2. html5移动web开发笔记

    width指定虚拟窗口的屏幕宽度大小. height指定虚拟窗口的屏幕高度大小. initial-scale 指定初始缩放比例. maximum-scale指定允许用户缩放的最大比例. minimum ...

  3. Linux中iptables设置详细(转)

    无论如何,iptables是一个需要特别谨慎设置的东西,万一服务器不在你身边,而你贸然设置导致无法SSH,那就等着被老板骂吧,呵呵... 以下内容是为了防止这种情况发生而写的,当然很初级,不过一般服务 ...

  4. 图解equals与hashcode方法相等/不相等的互相关系

    图解:比如equals相等的箭头指向hashcode相等,表示equals相等那么必有hashcode相等.而有两个箭头指向别人的表示可能是其中之一,比如hashcode相等,那么有可能equals相 ...

  5. motto3

    在我看来,最努力的人不一定能收获最好的,但不努力的人是必定收获不到任何东西的. 所以,园主,你要会努力才行.否则,你会累死的. 用心去学,用脑去想,认真对待每一件事,聪明一点,不要太愚蠢.

  6. Currency Exchange(Bellman-ford)

    Currency Exchange Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 21349   Accepted: 765 ...

  7. FastJSON 简介及其Map/JSON/String 互转

    在日志解析,前后端数据传输交互中,经常会遇到 String 与 map.json.xml 等格式相互转换与解析的场景,其中 json 基本成为了跨语言.跨前后端的事实上的标准数据交互格式.应该来说各个 ...

  8. HTML前端--各种小案例

    掬一捧清水,放逐在江河,融入流逝的岁月,将心洗净; 捻一缕心香,遥寄在云端,在最深的红尘里重逢,将心揉碎; 望一程山水,徘徊在月下,在相思渡口苦守寒冬,将心落寞. 案例一: 隐藏扩展域,并去掉afte ...

  9. 【Spring】Spring系列6之Spring整合Hibernate

    6.Spring整合Hibernate 6.1.准备工作 6.2.示例 com.xcloud.entities.book com.xcloud.dao.book com.xcloud.service. ...

  10. jquery validate自定义checkbox验证规则和样式

    参考:http://blog.csdn.net/xh16319/article/details/9987847 自定义checkbox验证,“检查checkbox是否选中” jQuery.valida ...