世界真的很奇妙,分久必合,合久必分。
计算机发展到今天,多国之间的交流日益广泛,软件本地化是重大趋势。如果减少本地化工作就是一件值得考虑的事情。
软件本地化要解决的真正问题就是如何来处理不同的字符集。要知道,单字节字符是一个8位的数据来表示的。 因此,它最多能表示256个字符。 全世界那么多个国家,256个怎么够。 因此人们提出了双字节(DBCS)来解决这个问题。
单字节与双字节字符集 ----->多字符集
当表示英文或某些符号的时候,就采用一个字节来表示,而当表示日文,中文等字符的时候,就采用两个位来表示。 可想而知,我们不可以再像操作单字节字符那样通过 pChar++;来遍历每一个字符。
为此,MS提供了CharNext,CharPre作为遍历工具。 不过这些函数让人头疼。
Unicode应运而生。它采用了两个字节来表示一个字符,不管是汉字还是英文。 统一一样。 两个字节即为16位,表示的数有65536个。 而世界各国的符号加起来才用了35000个左右,困此,足够了。
为什么我们要使用Unicode
当开发应用程序时,当然应该考虑利用Unicode的优点。即使你不打算让你的程序本地化,开发时也应该将Unicde放在心上,肯定可以简化你将来的代码转换工作。此外,Unicde还有以下功能。
1、可以很容易地在不同语言之彰进行数据交换。
2、使你能够分配支持所有语言的单个二进制.exe文件或dll文件。
3、提高应用程序的运行效率。
WINDOWS 2000上的Unicode
WINDOWS 2000是采用Unicode从头开发的。 所有与字符串相关的操作都会用到Unicode。当然,WINDOWS 2000的API都接受多字符集和Unicode字符集的参数。但是,只有Unicode的函数是实现了的。而多字符集的函数则是先将Unicode字符集转换成Unicode,然后再交给Unicode的函数处理。可知,采用Unicode调用API,速度会快不少。 同样,返回字符串的API函数也做同样的转换工作。
系统中会存在两套API 拿CreateFile为例。则有如下定义
#ifdef UNICODE
#define CreateFileW CreateFile
#else
#define CreateFileA CreateFile
#endif
当我们调用 CreateFile的时候,系统便会根据你是否要UNICODE而选择正常的函数。
而当你调用CreateFileA时,则有
调用CreateFileA---> 将多字符集参数转换为Unicode ---> 调用 CreateFileW
白白地多了转换工作。因此,采用Unicode编程,可以提高效率。
还有一些关于WINDOWS 98的就不介绍了。。需要知道的就是,WIN 98不支持Unicode,所以,强制调用W结尾的函数再用GetLastError()取得错误信息,你会发现提示你此函数没有实现。
WINDOWS CE则是完完全全的Unicode操作系统,不支持ANSI.....
如何使用UNICODE
数据类型
为了和ANSI有所区别,UNICODE版本的数据类型显然会不一样.
char wchar_t
而wchar_t的定义为 typedef unsigned shot wchar_t
可见,它是16位的。
而对于常用的字符串操作函数,对比如下
strcpy wcscpy
strcat wcscat
。。。
str 被换成了wcs 即 wide character string的缩写
上面是C运行期库的定义,由于MS提供的C运行期库与ANSI标准是一样的。所以上面的宽字符操作依然对WIN 98有效。
对于UNICODE的使用,我们则不能直接使用上面的函数,因为这样的话,ANSI/Unicode源码转换时你会哭掉。
于是,我们应该使用像
#ifdef UNICODE
#define _strcpy wcscpy
#else
#define _strcpy strcpy
#endif
这样的宏来使用每一个函数,而 TChar.h 头文件已经帮我们做到了。只需包含它,并使用正确的经过宏控制的函数名和类型。就可以很轻松地实现。。。
对于字符串的赋值。
char* p = "ook";
wchar_t *p = "ook";//错误
而应该是
wchart_t *P = L"ook";//L表示宽字符。
当然,我们也不能直接这样用。而是要用 TEXT 宏
用法如下 TCHAR *P = TEXT("ook");
定义类似于下面这样。
#ifdef UNICODE
typedef wchar_t TCHAR
#define TEXT(X) L##X
#else
typedef char TCHAR
#define TEXT(X)
#endif
这样就能正确对应了。
总结一下编写支持ANSI/UNICODE编译的原码规则。
#将文本串视为字符数组,而不是char数组或BYTE数组。(因为TCHAR的长度不固定)
#将通用数据类型(TCHAR,PTSTR)用于文本字符和字符串
#将显式数据类型(BYTE,PBYTE)用于字节,字节指针和数据缓存
#将TEXT宏用于原义字符和字符串。
#执行全局性替换(例如用PTSTR替换PSTR)
#修改字符串运算问题。例如计算数组大小时,应该用sizeof(szBubffer)/szBuffer[0] ;
#include <Windows.h> #include <tchar.h> #include <Shlwapi.h> #include <stdio.h> //宽字节 BOOL StringReversW(PWSTR pWchar) { PWSTR pEndStr = pWchar+wcslen(pWchar)-1; WCHAR pChar; while(pWchar<pEndStr) { pChar = *pWchar; *pWchar = *pEndStr; *pEndStr = pChar; pWchar++; pEndStr--; } return TRUE; } //多字节 //转换后交给宽字节,再将结果转换回多字节 BOOL StringReversA(PSTR pchar) { PWSTR pWchar; int nLenOfWideChar; BOOL ok = FALSE; nLenOfWideChar = MultiByteToWideChar(CP_ACP,0,pchar,-1,NULL,0); pWchar = (WCHAR*)HeapAlloc(GetProcessHeap(),0,nLenOfWideChar*sizeof(WCHAR)); if(!pWchar) return FALSE; MultiByteToWideChar(CP_ACP,0,pchar,-1,pWchar,nLenOfWideChar); ok= StringReversW(pWchar); if(ok) { WideCharToMultiByte(CP_ACP,0,pWchar,-1,pchar,strlen(pchar),NULL,NULL); } HeapFree(GetProcessHeap(),0,(LPVOID)pWchar); return ok; } //未经转换函数转换。。。。 BOOL StringRevers_(TCHAR* pWchar) { TCHAR* pEndStr = pWchar+ _tcslen(pWchar)-1; TCHAR pChar; while(pWchar<pEndStr) { pChar = *pWchar; *pWchar = *pEndStr; *pEndStr = pChar; pWchar++; pEndStr--; } return TRUE; } #ifdef UNICODE #define StringRevers StringReversW #else #define StringRevers StringReversA #endif int _tmain() { TCHAR pStr[]=TEXT("哈哈,这个东西好,ok?"); StringRevers_(pStr); //StringRevers(pStr); printf("%d",sizeof(TEXT("哈哈,这个东西好,ok?"))); MessageBox(NULL,pStr,NULL,MB_OK); return 0; }
此程序便支持ANSI/UNICODE,并且输出无异常。 可以将StringRevers_(pStr);屏蔽,将 //StringRevers(pStr);打开,并在ANSI/UNICODE下编译看效果。 另外,输出到控制台的结果,也说明了使用的字长不一样。。。
关于用到的两个转换函数,可以查MSDN。
总结完毕。。。打完收工。!!!!