【转】VC下的Unicode编程
转自http://www.leewei.org/?p=1304
UniCode简述
在Windows下用VC编程,如果编写的程序要在多种语言环境下运行(比如日文、中文、葡萄牙文等),使用VC默认的MBCS编译选项就会出现乱码,甚至导致程序崩溃。要克服这一缺点,就需要使用Unicode编程,简要说明一下Unicode:
Unicode也是一种字符编码方法,它占用两个字节(0000H—FFFFH),容纳65536个字符,这完全可以容纳全世界所有语言文字的编码。在Unicode里,所有的文字都按一个字符来处理,它们都有一个唯一的Unicode码。
Windows NT及后续系统的内核都是基于Unicode的。在Windows内核中,宏UNICODE指示是否启用Unicode,而C++是根据_UNICODE宏来判断的,因此在编程中我们要把这两个宏写进预处理参数里。
比如在tchar.h头文件中,有如下声明:
#define _T(x) __T(x) #ifdef _UNICODE
typedef wchar_t TCHAR;
#define __T(x) L##x
#else
typedef char TCHAR;
#define __T(x) x
#endif
而在winnt.h头文件中,定义了如下数据类型:
typedef char CHAR, *LPSTR;
typedef CONST CHAR *LPCSTR, *PCSTR; typedef unsigned short WCHAR,*LPWSTR; // 16-bit UNICODE character
typedef CONST WCHAR *LPCWSTR, *PCWSTR; //
#ifdef UNICODE
typedef WCHAR TCHAR, *PTCHAR;
typedef LPWSTR LPTCH, PTCH;
typedef LPWSTR PTSTR, LPTSTR;
typedef LPCWSTR LPCTSTR;
#define __TEXT(quote) L##quote
#else
typedef char TCHAR, *PTCHAR;
typedef LPSTR LPTCH, PTCH;
typedef LPSTR PTSTR, LPTSTR;
typedef LPCSTR LPCTSTR;
#define __TEXT(quote) quote
#endif /* UNICODE */
实际上Win32 API有两个版本。一个版本接受MBCS字符串,另一个接受Unicode字符串。例如:其实根本没有SetWindowText()这个API函数,相反,有SetWindowTextA()和SetWindowTextW()。后缀A表明这是MBCS函数,后缀W表示这是Unicode版本的函数。这些API函数的头文件在winuser.h中声明,下面例举winuser.h中的SetWindowText()函数的声明部分:
#ifdef UNICODE
#define SetWindowText SetWindowTextW
#else
#define SetWindowText SetWindowTextA
#endif // !UNICODE
UniCode实战
在VC6.0下使用Unicode的步骤如下:
1、project->Settings…->C/C++->Preprocessor Definitions,删除_MBCS,然后添加_UNICODE,UNICODE。
2、project->Settings…->Link->Category选择Output,Entry-point Symbol栏填入wWinMainCRTStartup。
【注】如果是不是exe工程(比如DLL或LIB),不执行第二个步骤,否则会出现warning LNK4086错误。
C++使用wchar_t来表示一个宽字符,它在内部被定义为unsigned short,占两个字节。相对于普通字符,C++有一整套的宽字符操纵函数, 以下是一份宽字符处理函数函数与普通函数对照表:
宽字符处理函数函数与普通函数对照表
字符分类:
宽字符函数 普通C函数 描述
iswalnum() isalnum() 测试字符是否为数字或字母
iswalpha() isalpha() 测试字符是否是字母
iswcntrl() iscntrl() 测试字符是否是控制符
iswdigit() isdigit() 测试字符是否为数字
iswgraph() isgraph() 测试字符是否是可见字符
iswlower() islower() 测试字符是否是小写字符
iswprint() isprint() 测试字符是否是可打印字符
iswpunct() ispunct() 测试字符是否是标点符号
iswspace() isspace() 测试字符是否是空白符号
iswupper() isupper() 测试字符是否是大写字符
iswxdigit() isxdigit() 测试字符是否是十六进制的数字 大小写转换:
宽字符函数 普通C函数 描述
towlower() tolower() 把字符转换为小写
towupper() toupper() 把字符转换为大写 字符比较:
宽字符函数 普通C函数 描述
wcscoll() strcoll() 比较字符串 日期和时间转换:
宽字符函数 描述
strftime() 根据指定的字符串格式和locale设置格式化日期和时间
wcsftime() 根据指定的字符串格式和locale设置格式化日期和时间, 并返回宽字符串
strptime() 根据指定格式把字符串转换为时间值, 是strftime的反过程 打印和扫描字符串:
宽字符函数 描述
fprintf()/fwprintf() 使用vararg参量的格式化输出
fscanf()/fwscanf() 格式化读入
printf() 使用vararg参量的格式化输出到标准输出
scanf() 从标准输入的格式化读入
sprintf()/swprintf() 根据vararg参量表格式化成字符串
sscanf() 以字符串作格式化读入
vfprintf()/vfwprintf() 使用stdarg参量表格式化输出到文件
vprintf() 使用stdarg参量表格式化输出到标准输出
vsprintf()/vswprintf() 格式化stdarg参量表并写到字符串 数字转换:
宽字符函数 普通C函数 描述
wcstod() strtod() 把宽字符的初始部分转换为双精度浮点数
wcstol() strtol() 把宽字符的初始部分转换为长整数
wcstoul() strtoul() 把宽字符的初始部分转换为无符号长整数 多字节字符和宽字符转换及操作:
宽字符函数 描述
mblen() 根据locale的设置确定字符的字节数
mbstowcs() 把多字节字符串转换为宽字符串
mbtowc()/btowc() 把多字节字符转换为宽字符
wcstombs() 把宽字符串转换为多字节字符串
wctomb()/wctob() 把宽字符转换为多字节字符 输入和输出:
宽字符函数 普通C函数 描述
fgetwc() fgetc() 从流中读入一个字符并转换为宽字符
fgetws() fgets() 从流中读入一个字符串并转换为宽字符串
fputwc() fputc() 把宽字符转换为多字节字符并且输出到标准输出
fputws() fputs() 把宽字符串转换为多字节字符并且输出到标准输出串
getwc() getc() 从标准输入中读取字符, 并且转换为宽字符
getwchar() getchar() 从标准输入中读取字符, 并且转换为宽字符
None gets() 使用fgetws()
putwc() putc() 把宽字符转换成多字节字符并且写到标准输出
putwchar() getchar() 把宽字符转换成多字节字符并且写到标准输出
None puts() 使用fputws()
ungetwc() ungetc() 把一个宽字符放回到输入流中 字符串操作:
宽字符函数 普通C函数 描述
wcscat() strcat() 把一个字符串接到另一个字符串的尾部
wcsncat() strncat() 类似于wcscat(), 而且指定粘接字符串的粘接长度.
wcschr() strchr() 查找子字符串的第一个位置
wcsrchr() strrchr() 从尾部开始查找子字符串出现的第一个位置
wcspbrk() strpbrk() 从一字符字符串中查找另一字符串中任何一个字符第一次出现的位置
wcswcs()/wcsstr() strchr()在一字符串中查找另一字符串第一次出现的位置
wcscspn() strcspn() 返回不包含第二个字符串的的初始数目
wcsspn() strspn() 返回包含第二个字符串的初始数目
wcscpy() strcpy() 拷贝字符串
wcsncpy() strncpy() 类似于wcscpy(), 同时指定拷贝的数目
wcscmp() strcmp() 比较两个宽字符串
wcsncmp() strncmp() 类似于wcscmp(), 还要指定比较字符字符串的数目
wcslen() strlen() 获得宽字符串的数目
wcstok() strtok() 根据标示符把宽字符串分解成一系列字符串
wcswidth() None 获得宽字符串的宽度
wcwidth() None 获得宽字符的宽度 另外还有对应于memory操作的 wmemcpy(),wmemchr(),wmemcmp(),wmemmove(),wmemset()。
Unicode编程中,如果需要声明一个宽字符串,需要这样写:
wchar_t *wstr = L”Hello”;
其中字符”L”告诉编译器你要构造的是一个宽字符串,”L”和字符串之间不能有空格。
虽然上述声明字符串的代码是正确的,但是并不提倡这样做,因为程序可移植性太差。
还记得前面介绍的几个宏么?_T(x)会在_UNICODE定义了的情况下被扩展为L##x, 而在一般情况下被扩展为x;TCHAR则分别被替换为wchar_t和char。因此我们可以这样写:
TCHAR *str = _T(“Hello”);
这样,如果_UNICODE宏被定义了,则它被扩展为:
wchar_t *wstr = L”Hello”;
否则,在默认情况下被扩展为:
char *str = “Hello”;
如果需要写一个库,而且要分别提供Unicode和非Unicode版本,那么仅仅许多修改两个UNICODE宏就可以了,不需要修改任何代码。
迁移到Unicode
如果非常不幸,你的项目在一开始没有被设计为使用Unicode(没有使用_T()宏和TCHAR等类型),而现在出于国际化的需要要使其支持Unicode,那么在添加两个UNICODE宏和函数入口点后会可能会出现无数个编译错误(我遇到过566个的)。虽然修改的方式根据项目而不同,但也多少有点相似之处,有步骤地做总比漫无目的得改好。
1、搜索所有的AfxMessagebox和Messagebox函数,将其中的字符串加上_T()宏。
2、搜索所有的str.Format函数,为第一个参数加上_T()宏。
3、为字符串常量加上_T()宏。
4、将strlen、strcpy等函数替换为wcslen、wcscpy等宽字符版本。
5、如果wcsncpy、wcsncmp等函数的第三个参数是sizeof(dst),那么现在就要改为sizeof(dst)/2,或者自定义一个宏tsizeof来实现。
6、如果某个函数确实需要char*等类型的参数,使用T2A()宏对参数进行转换,并在所在函数开头添加”USES_CONVERSION;”。
7、查找所有的char* p = (LPSTR)(LPCTSTR)CString这样的强制转换代码,并用char *p = T2A(CString);代替。
通常修改完以上内容,再次编译时错误应该减少了大半了,现在再一个一个地对照修改就容易多了。
最后,配置文件也要存储为Unicode的形式。Unicode的文件头有个0xFEFF标识,如果你是通过::WritePrivateProfileString()来写入配置文件的,那么只需要在调用此API之前往文件里写入0xFEFF文件头,此后WritePrivateProfileString会自动将后续内容保存成Unicode的形式。为了简单,可以讲程序中调用的::WritePrivateProfileString()全都替换成如下改写版本即可:
static BOOL _WritePrivateProfileString(LPCTSTR lpAppName, // section name
LPCTSTR lpKeyName, // key name
LPCTSTR lpString, // string to add
LPCTSTR lpFileName // initialization file
)
{
FILE *fp;
fp = _tfopen(lpFileName, _T("r"));
if (fp == NULL)
{
fp=_tfopen(lpFileName, _T("w+b")); wchar_t m_strUnicode[];
m_strUnicode[] = wchar_t(0XFEFF);
fputwc(*m_strUnicode,fp);
}
fclose(fp); return ::WritePrivateProfileString(lpAppName, lpKeyName, lpString, lpFileName);
}
【转】VC下的Unicode编程的更多相关文章
- VC++下的Unicode编程
ASCII是用来表示英文字符的一种编码规范.每个ASCII字符占用1个字节,因此,ASCII编码可以表示的最大字符数是255(00H—FFH). 其实,英文字符并没有那么多,一般只用前128个(00H ...
- VC的UNICODE 编程
简介 如果你编写的程序是针对非英语国家的用户,如中国.日本.东欧和中东地区,那么你一定要熟悉 UNICODE 字符集.尤其是用 Visual C++/MFC 编写针对上述国家和地区的用户的程序时,如果 ...
- 【转】VC++的Unicode编程
转自http://www.cnblogs.com/kex1n/archive/2010/03/15/2286510.html 原始出处http://www.vckbase.com/document/v ...
- VC++的Unicode编程
本文来自:http://tech.ddvip.com/2007-03/117395585321221.html 一.什么是Unicode 先从ASCII说起,ASCII是用来表示英文字符的一种编码规范 ...
- 【Windows编程】系列第四篇:使用Unicode编程
上一篇我们学习了Windows编程的文本及字体输出,在以上几篇的实例中也出现了一些带有“TEXT”的Windows宏定义,有朋友留言想了解一些ANSI和Unicode编程方面的内容,本章就来了解和学习 ...
- 在VC下采用ADO实现BLOB(Binary)数据的存储,读取,修改,删除。
在VC下采用ADO实现BLOB(Binary)数据的存储,读取,修改,删除. 作者:邵盛松 2009-09-05 前言 1关于的BLOB(Binary)数据的存储和读取功能主要参考了MSDN上的一篇& ...
- LINUX下C语言编程基础
实验二 Linux下C语言编程基础 一.实验目的 1. 熟悉Linux系统下的开发环境 2. 熟悉vi的基本操作 3. 熟悉gcc编译器的基本原理 4. 熟练使用gcc编译器的常用选项 5 .熟练使用 ...
- DELPHI下的SOCK编程(转)
DELPHI下的SOCK编程 本文是写给公司新来的程序员的,算是一点培训的教材.本文不会涉及太多的编程细节,只是简单讲解在DELPHI下进行Winsock编程最好了解的知识. 题外话:我认为 ...
- linux 操作系统下c语言编程入门
2)Linux程序设计入门--进程介绍 3)Linux程序设计入门--文件操作 4)Linux程序设计入门--时间概念 5)Linux程序设计入门--信号处理 6)Linux程序设计入门--消息管理 ...
随机推荐
- [Unity3D]Unity3D游戏开发之Lua与游戏的不解之缘终结篇:UniLua热更新全然解读
---------------------------------------------------------------------------------------------------- ...
- 算法笔记_065:分治法求逆序对(Java)
目录 1 问题描述 2 解决方案 2.1 蛮力法 2.2 分治法(归并排序) 1 问题描述 给定一个随机数数组,求取这个数组中的逆序对总个数.要求时间效率尽可能高. 那么,何为逆序对? 引用自百度 ...
- Android中Word转Html
一.POI方式 1.先看word效果图 2.再看下在android上使用WebView显示的效果 3. 生成的html的代码,如下: <html> <head> <M ...
- RxJava API使用示例
概述 RxJava API示例代码,可离线查看rxjava1.0大部分API的marble图,描述,示例代码,并支持示例代码实时输出及展示执行结果. 详细 代码下载:http://www.demoda ...
- bootstrap 学习笔记 轮播(Carousel)插件
Bootstrap轮播(carousel)插件是一种灵活的响应式的向站点添加滑块的方式.除些之外,内容也是足够灵活的,可以是图像,内嵌框架,视频或者其他您想要旋转的任何类型的内容. 示例: 下面是不念 ...
- Bootstrap学习 进度条
本文将介绍Bootstrap进度条,在本文中你将看到如何使用Bootstrap创建加载,重定向或动作状态的进度条 bootstrap进度条使用CSS3过渡和动画来获得该效果.Internet Expl ...
- Image.Save出错 GDI 一般错误
一般是路径问题 需要转化为绝对路径 \转换为/ if (HttpContext.Current!=null) outPath = HttpContext.Current.Server.MapPath( ...
- Spring技术内幕:Spring AOP的实现原理(三)
生成SingleTon代理对象在getSingleTonInstance方法中完毕,这种方法时ProxyFactoryBean生成AopProxy对象的入口.代理对象会封装对target目标对象的调用 ...
- JavaScript中使用JSON,即JS操作JSON总结
JSON(JavaScript Object Notation 对象标记) 是一种轻量级的数据交换格式,采用完全独立于语言的文本格式,是理想的数据交换格式.同时,JSON是 JavaScript 原生 ...
- css - 当文本内容长度超出屏幕宽度时,以省略号代替
<style> .ellipsis{ text-overflow: ellipsis; overflow: hidden; white-space: nowrap; } </styl ...