宽字符

阅读了 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 的互相转换。

  1. std::string narrow(const wchar_t *s);
  2. std::wstring widen(const char *s);
  3. std::string narrow(const std::wstring &s);
  4. std::wstring widen(const std::string &s);
  5. wchar_t *widen(const char *s);
  6. char *narrow(const wchar_t *s);

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

  1. CopyFileW(widen(existing_file).c_str(),
  2. widen(new_file).c_str(),
  3. TRUE);

函数实现

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

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

  1. #include <windows.h>
  2. wchar_t *widen(const char *s, wchar_t *ws, size_t ws_size) {
  3. size_t required_size;
  4. // Get the required buffer size
  5. required_size = MultiByteToWideChar(CP_UTF8, 0, s, -1, ws, 0);
  6. if (required_size >= ws_size)
  7. return NULL;
  8. // Convert NULL terminated UTF-8 string to the UTF-16 (wide character) string
  9. if (MultiByteToWideChar(CP_UTF8, 0, s, -1, ws, ws_size) == 0)
  10. return NULL;
  11. return ws;
  12. }
  13. char *narrow(const wchar_t *ws, char *s, size_t s_size) {
  14. size_t required_size;
  15. // Get the required buffer size
  16. required_size = WideCharToMultiByte(CP_UTF8, 0, ws, -1, s, 0, NULL, NULL);
  17. if (required_size >= s_size)
  18. return NULL;
  19. // Convert NULL terminated UTF-8 string to the UTF-16 (wide character) string
  20. if (WideCharToMultiByte(CP_UTF8, 0, ws, -1, s, s_size, NULL, NULL) == 0)
  21. return NULL;
  22. return s;
  23. }

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

  1. void print_wstring(const wchar_t *ws) {
  2. DWORD w;
  3. WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE), ws, wcslen(ws), &w, NULL);
  4. }
  5. int main(void) {
  6. char cc[] = "\xE4\xB8\x80y\n";
  7. wchar_t utf16[512];
  8. wchar_t uu[] = L"一\n";
  9. print_wstring(widen(cc, utf16, sizeof(utf16)));
  10. print_wstring(uu);
  11. system("pause");
  12. return 0;
  13. }

源代码中的 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. phpcms 网站迁移服务器

    相信很多人不知道怎么去把PHPCMS V9进行搬家 在本地测试好的phpcms v9网站需要搬到服务器上,可以用以下方法: 1.上传所有的程序文件(如果主机支持压缩包在线解压,那么就打成zip的包,f ...

  2. Java操作xml文件

    Bbsxml.java public class Bbsxml { private String imgsrc; private String title; private String url; p ...

  3. Java设计原则:面向接口的设计

    前言:在一个面向对象的系统中,系统的各种功能是由许许多多的不同对象协作完成的.在这种情况下,各个对象内部是如何实现对系统设计人员来说就不那么重要:而各个对象之间的协作关系则成为系统设计的关键.小到不同 ...

  4. derby数据库操作比较难理解的错误及解决方法大全

    一.插入(INSERT时报错) 1.错误:java.sql.SQLIntegrityConstraintViolationException: 列“test”无法接受空值. 可能原因:建表时test列 ...

  5. JSON 与 String、Map、JavaBean互转

    JSON 与 String.Map.JavaBean互转 //解析远程登录用户信息 AttributePrincipal principal = AssertionHolder.getAssertio ...

  6. 在表单(input)中id和name的区别

    但是name在以下用途是不能替代的: 1. 表单(form)的控件名,提交的数据都用控件的name而不是id来控制.因为有许多name会同时对应多个控件,比如checkbox和radio,而id必须是 ...

  7. ++X 与 X++ 的区别

    <?php $x=10; echo ++$x; // 输出 11 $y=10; echo $y++; // 输出 10 $z=5; echo --$z; // 输出 4 $i=5; echo $ ...

  8. [Effective JavaScript 笔记]第16条:避免使用eval创建局部变量

    js中的eval函数是一个强大.灵活的工具.强大的工具容易被滥用,所以了解是值得的.(本人只用过它来处理json数据).错误使用eval函数的方式一:允许它干扰作用域.调用eval函数会将其参数作为j ...

  9. Coursera台大机器学习技法课程笔记03-Kernel Support Vector Machine

    这一节讲的是核化的SVM,Andrew Ng的那篇讲义也讲过,讲的也不错. 首先讲的是kernel trick,为了简化将低维特征映射高维特征后的计算,使用了核技巧.讲义中还讲了核函数的判定,即什么样 ...

  10. [ruby on rails] 跟我学之(7)创建数据

    通过form来创建数据,本章节将会涉及内容:创建form,用户重导向,渲染views 和 flash消息. 1. views初步 编辑 app/views/posts/index.html.erb这个 ...