下面我按问答的方式进行展开:

  • 什么叫本地化编程?

相信很多人都有这个疑问,什么叫本地化编程呢?我们知道英文字符大多数情况下在计算机内是占用一个字节的,也就是所谓的char型字符,即我们常用的ASCII码。每个码都占用一个char。但如果想使用中文字符呢?如果想使用日文字符呢?埃及文呢?火星文呢?

一个字节进行编码最多也就256个不同的字符,这个数量对于庞大的语言体系是不可能满足的,比如中文常用字就不止256个(中文博大精深)。这在我们计算机界当然是不允许的,我们讲究的是一切皆可建模,一切皆可存储,一切皆可编程。而如果使用不同的编码方式来编写软件,就会遇到这样的问题,当你用中文编码方式写的软件拿到美国去用的时候就会出现乱码的现象,因为他的计算机无法识别你的编码方式,你是以2个字节代表一个汉字,而他却解读为1个字节翻译为一个英文,这牛头不对马嘴。所以这个时候所谓的本地化编程说到底就是处理不同的字符集的。

  • 什么叫Unicode?

面对这种字符集百家争鸣的情况,计算机界的大佬们肯定不乐意了,自己编的软件如果不能销售全球甚至外太空那还有什么意义。所以Apple和Xerox公司在1988年建立的一个技术标准称为Unicode。1991年成立了一个集团机构负责Unicode的研发和推广。这个集团可以说笼络了所有IT界的大佬们,像什么Apple、HP、IBM、Microsoft、Oracle等等耳熟能详的名字。你可以去www.Unicode.org主页上看看。

Unicode字符串中的所有字符都是16位的,也就是2个字节。所以总共可以得到65546个字符,当然这是理论上的,还要去掉一些分类标志啊什么的,估计也能有65000个吧!这和256个可不止是翻了一倍,这下全世界任何一种语言都能够用它来进行编码了。在百度百科里搜索Unicode可以看到它目前为世界上语言的编码情况:http://baike.baidu.com/view/40801.htm

p_w_picpath

有兴趣的朋友可以仔细研究下还有哪些地方有空位,自己开发个什么鸟语的争取让Unicode集团填进去,呵呵~~

  • 如果在windows下编写Unicode代码?

windows早在windows2000以后的版本里使用Unicode进行全系统开发了,也就是用于创建窗口、显示文本、进行字符串操作等所有核心函数都需用Unicode字符串。可我们在进行windows编程时基本上没有考虑这个问题,这也就所谓的兼容能力,但这是付出了代价的,比如你传递一个ANSI字符串给使用Unicode字符串的函数,那么系统首先会把字符串转换成Unicode,然后把Unicode传递给操作系统,如果希望函数返回ANSI字符串,那么系统会把Unicode字符串转换成ANSI字符串,然后将结果返回给你的应用程序。所有这些转换操作都是在你看不到的情况下进行的。这都是需要占用系统时间和内存的。

所以,今后的程序应该尽量使用Unicode来进行编码,那么如何在windows平台下进行Unicode编码呢?Microsoft公司为Unicode设计了Windows API。只需要在你的源代码里定义两个宏(UNICODE和_UNICODE)就来使用Unicode编码了。那么这两个宏是怎么作用的呢?

首先让我们来看看_UNICODE宏:

我们都知道标准C头文件String.h里定义了一个wchar_t的数据类型(什么,你不知道,不知道google去),它就是一个Unicode字符的数据类型。

typedef unsigned short wchar_t;

例如,你可以定义一个Unicode的字符数组像这样:wchar_t szBuffer[100];

该语句所占用的空间不是100个字节,而是200个。当然像strcpy、strcat、strchr等字符串操作的常见函数也是无法对Unicode使用的,所以也有一组补充的函数:

char *strcat(char *, const char *);

wchar_t *wcscat(wchar_t *, const wchar_t *);

char *strchr(const char *, int);

wchar_t *wcschr(const wchar_t *, wchar_t);

char *strcpy(char *, const char *);

wchar_t *wcscpy(wchar_t *, const wchar_t);

int strcmp(const char *, const char *);

int wcscmp(const wchar_t *, const wchar_t *);

size_t strlen(const char *);

size_t wcslen(const wchar_t *);

可以发现所有的Unicode函数开头都有wcs这几个字母,wcs是宽字符串的英文缩写——width char string(哈哈~~)。那如果我的代码中即有char型的内容,也有wchar_t的内容,比如PM让我修改一份源代码,里面以前用的是char型,而现在PM要求所有新增的代码都用wchar_t型该怎么办呢?怎么在一份源代码里即允许char的,又可以编译wchar_t呢?这个时候就可以包含TChar.h这个头文件。TChar.h里就是一组宏,用于兼容ANSI和Unicode的。这个时候你就不要调用str或wcs类的函数了,直接使用TChar.h里的通用宏就可以了。如果在编译的源代码文件里定义了_UNICODE宏,那么TChar.h里的这组宏就会引用wcs这组函数,否则就引用str的这组。

比如在TChar.h中有_tcscpy这个宏,如果没有定义_UNICODE宏,那么它就会调用strcpy函数,如果定义了就调用wcscpy函数。

你甚至可以使用TChar.h中定义的通用字符类型TCHAR(是不是很眼熟,感觉以前看到过,但没用过。呵呵~~那就对了!!)。如果定义了_UNICODE,那么TCHAR的声明就是这样的:typedef wchar_t TCAHR;

如果没有定义_UNICODE,那么声明就是:typedef char TCHAR;

用TCHAR来定义字符串像这样:TCHAR szString[100];

也可以创建字符串指针:TCHAR *szError = "Error";

聪明人可能马上会说,上面这样定义编译器会把后面的字符串当作Unicode吗?按照MS公司C++编译器的默认设置是把他们当作ANSI字符的,因此,如果没有定_UNICODE,那么TCHAR就是char型的,没有什么问题,如果定义了_UNICODE,上面这行就会出错,因为给wchar_t赋值的是一个char型的字符串,那我想给它赋值为wchar_t型怎么办呢?只需要这样:TCHAR *szError = L"Error";

前面加一个大写的L就行了。告诉编译器这个字符串是一个Unicode。但现在又有问题,如果我像上面这样写,那么只有在定义了_UNICODE时才能正确编译,否则将会报错啊,谁也没看到过这种字符串的定义啊!所以为了解决这个问题在TChar.h中定义了另一个宏_TEXT宏,如果定义了_UNICODE,那么_TEXT宏定义如下:#define _TEXT(X) L##x。(那个##据说是粘贴符号,无所谓了,反正我也不关心),如果没有定义_UNICODE,那么_TEXT宏定义如下:#define _TEXT(X) x。

那下面就让我们来看看这个宏的强大吧!以后再定义字符串不用纠结于上面的问题了,直接这么写:TCHAR *szError = _TEXT("Error");

_TEXT也可以用于定义单个字符,比如:szError[0] = _TEXT('A');

接着让我们看看UNICODE宏吧:

Windows头文件定义了几个Unicode的数据类型:

WCHAR——Unicode字符

PWSTR——指向Unicode字符串的指针

PCWSTR——指向一个const类型的UNICODE字符串的指针

我们都知道windows的API经常提供两套函数原型,比如CreateWindowEx函数,就有两套原型CreateWindowExW和CreateWindowExA。初学者很少注意这个,因为我们都是直接用CreateWindowEx函数的,谁管它有几套原型呢!不过现在就让我们来关注一下吧。其实这两个原型基本上一致:

HWND WINAPI CreateWindowExW(

DWORD dwExStyle,

PCWSTR pClassName,

PCWSTR pWindowName,

DWORD dwStyle,

int x, int y,

int nWidth, int nHeight,

HWND hWndParent,

HMENU hMenu,

HINSTANCE hInstance,

PVOID pParam);

HWND WINAPI CreateWindowExA(

DWORD dwExStyle,

PCSTR pClassName,

PCSTR pWindowName,

DWORD dwStyle,

int x, int y,

int nWidth, int nHeight,

HWND hWndParent,

HMENU hMenu,

HINSTANCE hInstance,

PVOID pParam);

就上面标红的两个地方有区别,其实就是一个是Unicode的,一个是ANSI的。我们可以看看WinUser.h里对CreateWindowEx的宏定义其实是这样的:

#ifdef UNICODE

#define CreateWindowEx CreateWindowExW

#else

#define CreateWindowEx CreateWindowExA

#endif

有了这个定义,那么你只需要调用CreateWindowEx函数就行了,在需要用Unicode时就在源代码前面定义一个UNICODE就行了,不需要去修改CreateWindowEx函数。

这只是一个例子,windows提供的API大多数都是类似这样的定义,如字符串操作的一组函数windows也给出了通用的API:

lstrcat——将一个字符串置于另一个字符串的结尾处

lstrcmp——对两个字符串进行区分大小写的比较

lstrcmpi——对两个字符串进行不区分大小写的比较

lstrcpy——将一个字符串拷贝到内存中的另一个位置

lstrlen——返回字符串的长度(按字符数来计算,而不是字节数)

以上的函数当定义了UNICODE宏时会扩展为Unicode版,没有定义UNICODE时会扩展为ANSI版,所以以后尽量用这些吧!

还有一些转换字符大小写的函数,比如在C库里的tolower和toupper函数是无法正确转换Unicode字符的,那么就必须调用windows定义的函数来转换:

PTSTR CharLower(PTSTR pszString);

PTSTR CharUpper(PTSTR pszString);

也可以转换单个字符:

TCHAR cLowerChar = CharLower((PTSTR)szString[0]);

还有些类似的函数,比如:

DWORD CharLowerBuffer(PTSTR pszString, DWORD cchString);

DWORD CharUpperBuffer(PTSTR pszString, DWORD cchString);

BOOL IsCharAlpha(TCHAR ch);

BOOL IsCharAlphaNumeric(TCHAR ch);

BOOL IsCharLower(TCHAR ch);

BOOL IsCharUpper(TCHAR ch);

当然,还有printf函数族。大家看个例子就会明白怎么用了:(注意S的大小写)

char szA[100]; //An ANSI string buffer

WCHAR szW[100]; //A Unicode string buffer

sprintf(szA, "%s", "ANSI Str");

sprintf(szA, "%S", L"Unicode Str");

swprintf(szW, L"%s", L"Unicode Str");

swprintf(szW, L"%S", "ANSI Str");

windows还有很多地方也是这样的,不光windows系统,其他的系统也存在这样的情况,万变不离其宗,中华文化博大精深,大家发挥自己的慧眼去用心发现吧~~

参考至googlebaidubing《Windows核心编程》