转自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编程的更多相关文章

  1. VC++下的Unicode编程

    ASCII是用来表示英文字符的一种编码规范.每个ASCII字符占用1个字节,因此,ASCII编码可以表示的最大字符数是255(00H—FFH). 其实,英文字符并没有那么多,一般只用前128个(00H ...

  2. VC的UNICODE 编程

    简介 如果你编写的程序是针对非英语国家的用户,如中国.日本.东欧和中东地区,那么你一定要熟悉 UNICODE 字符集.尤其是用 Visual C++/MFC 编写针对上述国家和地区的用户的程序时,如果 ...

  3. 【转】VC++的Unicode编程

    转自http://www.cnblogs.com/kex1n/archive/2010/03/15/2286510.html 原始出处http://www.vckbase.com/document/v ...

  4. VC++的Unicode编程

    本文来自:http://tech.ddvip.com/2007-03/117395585321221.html 一.什么是Unicode 先从ASCII说起,ASCII是用来表示英文字符的一种编码规范 ...

  5. 【Windows编程】系列第四篇:使用Unicode编程

    上一篇我们学习了Windows编程的文本及字体输出,在以上几篇的实例中也出现了一些带有“TEXT”的Windows宏定义,有朋友留言想了解一些ANSI和Unicode编程方面的内容,本章就来了解和学习 ...

  6. 在VC下采用ADO实现BLOB(Binary)数据的存储,读取,修改,删除。

    在VC下采用ADO实现BLOB(Binary)数据的存储,读取,修改,删除. 作者:邵盛松 2009-09-05 前言 1关于的BLOB(Binary)数据的存储和读取功能主要参考了MSDN上的一篇& ...

  7. LINUX下C语言编程基础

    实验二 Linux下C语言编程基础 一.实验目的 1. 熟悉Linux系统下的开发环境 2. 熟悉vi的基本操作 3. 熟悉gcc编译器的基本原理 4. 熟练使用gcc编译器的常用选项 5 .熟练使用 ...

  8. DELPHI下的SOCK编程(转)

    DELPHI下的SOCK编程      本文是写给公司新来的程序员的,算是一点培训的教材.本文不会涉及太多的编程细节,只是简单讲解在DELPHI下进行Winsock编程最好了解的知识. 题外话:我认为 ...

  9. linux 操作系统下c语言编程入门

    2)Linux程序设计入门--进程介绍 3)Linux程序设计入门--文件操作 4)Linux程序设计入门--时间概念 5)Linux程序设计入门--信号处理 6)Linux程序设计入门--消息管理  ...

随机推荐

  1. mysql导入数据乱码的解决

    #mysql -uroot -p -hlocalhost --default-character-set=utf8; mysql>use db_name; mysql>source /ho ...

  2. Unity3d 显示IOS基本的游戏中心脚本

    using UnityEngine; using UnityEngine.SocialPlatforms; public class Startup : MonoBehaviour { // we'l ...

  3. 常见Style 对象属性值

    Style对象的主要需要关注的属性分为4类,下面分别介绍下: Background 属性 backgroundColor 属性设置元素的背景颜色 Object.style.backgroundColo ...

  4. JMeter3.0配置

    解压apache-jmeter-3.0.zip文件至c盘,本文解压至C:\jmeter3目录下. 桌面上选择“我的电脑”(右键),高级, 环境变量, 在“系统变量”--->“新建”, 在变量名中 ...

  5. SchemaExport

    不在xml中配置         <!-- Drop and re-create the database schema on startup         <property name ...

  6. Fiddler-抓取安卓手机APP请求地址

    第一步:下载神器Fiddler,下载链接: http://fiddler2.com/get-fiddler 下载完成之后,傻瓜式的安装一下了! 第二步:设置Fiddler打开Fiddler,     ...

  7. [机器学习实战] 决策树ID3算法

    1. 决策树特点: 1)优点:计算复杂度不高,输出结果易于理解,对中间值的缺失不敏感,可以处理不相关特征数据. 2)缺点:可能会产生过度匹配问题. 3)适用数据类型:数值型和标称型. 2. 一般流程: ...

  8. golang使用sqlite

    安装问题 在import sqlite的时候,golang build 出现以下错误, exec: "gcc": executable file not found in %PAT ...

  9. Js日常笔记之变量删除

    在Javascript是可以使用delete来手动删除变量,通过这样的方法让GC来回收内存,但在JS中并不是所有的对象都可以被删除的 JS中通过 var\function 声明因含有DontDelet ...

  10. 接收广播BroadcastReceiver

    Broadcast Receiver用于接收并处理广播通知(broadcast announcements).多数的广播是系统发起的,如地域变换.电量不足.来电来信等.程序也可以播放一个广播.程序可以 ...