Windows 程序支持 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 存储而不使用宽字符类型。在需要传入宽字符类型时进行转换,实现 widen
和 narrow
两种类型的函数,完成 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 中,包含 widen
和 narrow
两种类型函数的实现,并对标准库函数进行了包装,使得可以编写跨平台支持 Unicode 的程序。
UTF-8 Everywhere 中也提到可以使用 Windows 的 MultiByteToWideChar 和 WideCharToMultiByte 两个 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的更多相关文章
- windows控制台程序——关于UNICODE字符的总结(转)
前言:从Windows NT/2000开如,Windows系统已经是一个标准的UNICODE系统,系统内部所有字符串存储及操作均使用UNICODE编码.因此Win32 API都是UNICODE版本的, ...
- C++不支持Unicode,即使utf8
今天,字符串unicode我们已经不需要常理的理由,但是,一些有编程语言的悠久历史.这仍然是一个头疼. 尽管第三方库支持的假设,C++事实上没有真正有效地支持unicode.即使utf8.(注意:本文 ...
- Windows程序员必须知道的字符编码和字符集
字符编码 (Character encoding) 在存储和传递文本过程中,为了使得所有电脑都能够正确的识别出文本内容,需要有一个统一的规则. 2. 字符集 (Character Set) ) 一般 ...
- VC6.0支持UNICODE的步骤
针对MFC程序的开发,支持Unicode一共需要三步: Step1 设置->C/C++预处理定义中,删除_MBCS,添加_UNICODE,UNICODE. Step2 设置->Link-& ...
- 使你的C/C++代码支持Unicode(CRT字符串处理的所有API列表,甚至有WEOF字符存在)
悉Microsoft支持Unicode的方式. 它的主要目的是方便你查询相关的数据类型和函数,以及修正相应的拼写错误. I18nGuy 主页 XenCraft (Unicode 咨询公司) Engli ...
- windows程序设计基础知识
Win32 API(Application Programming Interface) Win32 API可认为是一个程序库,提供各式各样的与windows系统服务有关的函数. Win32 API是 ...
- Windows编程中UNICODE和_UNICODE定义问题
Windows编程中UNICODE和_UNICODE定义问题 先转一篇文章: 初学Windows SDK编程时碰到过这个问题,相信很多初学Windows编程的人也都碰到过,后来慢慢搞明白些了,但有时别 ...
- Linux下运行windows程序
现在Winxp停止了支持,那我们的windows程序是否可以再linux上执行呢,如下是一些参考的信息 在您的 Linux/Mac 操作系统上运行 Windows 软件 http://www.wine ...
- Windows程序----初识Windows程序
先来看一些励志名言来激励一下自己吧! 励志名言:每一发奋发奋的背后,必有加倍的赏赐 1.有无目标是成功者与平庸者的根本差别. 2.成功不是将来才有的,而是从决定去做的那一刻起,持续累积而成. 3.当 ...
随机推荐
- ModSecurity SQL注入攻击
ModSecurity是 一个入侵探测与阻止的引擎,它主要是用于Web应用程序所以也可以叫做Web应用程序防火墙.它可以作为Apache Web服务器的一个模块或单独的应用程序来运行.ModSecur ...
- hibernate的pojo和xml文件
- WPF 动态布局Grid
//开启线程加载 Action a = () => { ; ; var path = "../../face_img/"; var files = Directory.Get ...
- Software caused connection abort: recv failed 错误介绍
解决1: Software caused connection abort: recv failed java.net.SocketException: Software caused connect ...
- [Winform]一个简单的账户管理工具
最近一直觉得注册的账户越来越多,帐号密码神马的容易弄混.自己就折腾了一个简单的账户管理工具,其实实现也挺简单,将每个账户的密码及相关密码提示信息,经aes算法加密之后保存到数据库,当前登录用户可以查询 ...
- thinkphp中模板继承
模板继承是3.1.2版本添加的一项更加灵活的模板布局方式,模板继承不同于模板布局,甚至来说,应该在模板布局的上层.模板继承其实并不难理解,就好比类的继承一样,模板也可以定义一个基础模板(或者是布局), ...
- cocos2dx+lua注册事件函数详解
coocs2dx 版本 3.1.1 registerScriptTouchHandler 注册触屏事件 registerScriptTapHandler 注册点击事件 registerScriptHa ...
- 将JSON转成DataSet(DataTable)
方法1: /// <summary> /// 将JSON解析成DataSet只限标准的JSON数据 /// 例如:Json={t1:[{name:'数据name',type:'数据type ...
- python学习之最简单的用户注册及登录验证小程序
文章都是从我的个人博客上粘贴过来的哦,更多内容请点击 http://www.iwangzheng.com 正如很多同学所知道的,楼主开始学习python了,前进的道路曲曲折折,有荆棘也有陷阱,从最简单 ...
- VS2010调用Com组件
Com组件开发过程中用的不多,资料也不多,故记录开发Com组件中的部分问题. 在这一篇文章里,讲解了如何使用VS2010创建Com组件.现在基于该文章创建的Com组件接口,创建VC++项目来调用该接口 ...