代码页

转载一篇关于代码页的。
原址:http://hi.baidu.com/zhangweiguo/blog/item/895dd63f211377ce7c1e710d.html
2.1 Locale和LCID

Locale是指特定于某个国家或地区的一组设定,包括字符集,数字、货币、时间和日期的格式等。在Windows中,每个Locale可以用一个32位数字表示,记作LCID。在winnt.h中可以看到LCID的组成。它的高16位表示字符的排序方法,一般为0。在它的低16位中,低10位是primary
language的ID,高4位指定sublanguage。sublanguage被用来区分同一种语言的不同编码。下面是部分primary
language和sublanguage的常数定义:


#define LANG_CHINESE
0x04
#define LANG_ENGLISH 0x09
#define LANG_FRENCH 0x0c
#define
LANG_GERMAN 0x07


#define
SUBLANG_CHINESE_TRADITIONAL 0x01 // Chinese (Taiwan Region)
#define
SUBLANG_CHINESE_SIMPLIFIED 0x02 // Chinese (PR China)
#define
SUBLANG_ENGLISH_US 0x01 // English (USA)
#define SUBLANG_ENGLISH_UK 0x02 //
English (UK)


好,现在我们可以计算简体中文的LCID了,将sublanguage的常数左移10位,即乘上1024,再加上primary
language的常数:2*1024+4=2052,16进制是0804。美国英语是:1*1024+9=1033,16进制是0409。。繁体中文是1*1024+4=1028,16进制是0404。


2.2 代码页

每个Locale都联系着很多信息,可以通过GetLocalInfo函数读取。其中最重要的信息就是字符集了,即Locale对应的语言文字的编码。Windows将字符集称作代码页。


每个Locale可以对应一个ANSI代码页和一个OEM代码页。Win32
API使用ANSI代码页,底层设备使用OEM代码页,两者可以相互映射。


例如English
(US)的ANSI和OEM代码页分别为“1252 (ANSI - Latin I)”和“437 (OEM - United States)”。 Chinese
(PRC)的ANSI和OEM代码页都是“936 (ANSI/OEM - Simplified Chinese GBK)”。 Chinese
(TW)的ANSI和OEM代码页都是“950 (ANSI/OEM - Traditional Chinese Big5)”。


附录1中有一张很长的表。列出了我正在使用的Windows所支持的135个Locale的部分信息,包括
LCID、国家/地区名称、语言名称、语言缩写和对应的ANSI代码页。


2.3
系统Locale、用户Locale,再谈ANSI代码页

在Windows中,通过控制面板可以为系统和用户分别设置Locale。系统Locale决定代码页,用户Locale决定数字、货币、时间和日期的格式。这不是一个好的设计,后面会谈到它带来的问题。


使用GetSystemDefaultLCID函数和GetUserDefaultLCID函数分别得到系统和用户的LCID。有很多材料将这两个函数和另外两个函数混淆:GetSystemDefaultUILanguage和GetUserDefaultUILanguage。


GetSystemDefaultUILanguage和GetUserDefaultUILanguage得到的是您当前使用的Windows版本所带的UI资源的语言。


用户程序缺省使用的代码页是当前系统Locale的ANSI代码页,可以称作ANSI编码,也就是A版本的Win32
API默认的字符编码。对于一个未指定编码方式的文本文件,Windows会按照ANSI编码解释。


2.4 AppLocale

如果一个文本文件采用BIG5编码,系统当前的ANSI代码页是GBK。打开这个文件,就会显示乱码。例如“中文”在BIG5中的编码是A4A4、A4E5,这两个编码在GBK中对应的字符是“いゅ”。这是日文的两个平假名。


在Windows
XP平台有一个AppLocale程序,可以以指定的语言运行非Unicode程序。用Win32dsm打开看一看,其实它只是在运行程序前设置了两个环境变量。我们可以用个批处理文件模仿一下:


@ECHO OFF
SET
__COMPAT_LAYER=#ApplicationLocale
SET ApplocaleID=0404
start
notepad.exe


在简体中文平台,用这个批处理文件启动的记事本可以正确显示BIG5编码的文本文件。用它打开GBK编码的文本文件会怎么样?“中文”会被显示为“笢恅”。设置这两个环境变量会作用于当前进程和其子进程。Windows
2000平台不支持这个方法。


3
MBCS程序和Unicode程序


3.1
与字符编码有关的编译参数

让我们回到Win32
API。我们在程序中使用的Win32
API没有A/W后缀,Windows的头文件会根据编译参数UNICODE将没有后缀的函数名替换为A版本或W版本,例如:


#ifdef UNICODE
#define
CreateFile CreateFileW
#else
#define CreateFile
CreateFileA
#endif


C
RunTime库(CRT)使用_UNICODE和_MBCS来区分三套字符串处理函数,分别用于SBCS、MBCS和Unicdoe字符串。SBCS和MBCS分别指单字节字符串和多字节字符串。例如_tcsclen的3个版本分别为strlen、_mbslen和wcslen
,猜猜以下函数返回几?


strlen("VOIP网关");
_mbslen((unsigned
char *)"VOIP网关");
wcslen(L"VOIP网关");


答案是8、6、6。L"ANSI字符串"通知编译器将ANSI字符串转换为Unicode字符串,这是VC++编译器提供的一个小甜点。不过我们应该用宏:_T("ANSI字符串")。_T宏只在我们定义了_UNICODE时才转换。这样同一套代码既可以编译MBCS版本,也可以编译Unicode版本。


MFC用_UNICODE参数区分Unicode版本特有的代码,决定使用什么版本的导入库或静态库。


3.2
Unicode程序、MBCS程序和多语言支持

Unicode程序直接使用Unicode版本的CRT和Win32
API。Unicode程序的运行与当前的ANSI代码页没有关系。MBCS程序的运行依赖于ANSI代码页。如果设计者和使用者使用不同的代码页,就可能出现乱码。微软开发的程序大都是Unicode程序,不管我们怎样变换系统Locale,它们总能正常运行。


使用VCL类库的Delphi程序都是MBCS程序。VCL框架在程序启动会调用GetThreadLocale获取当前用户的LCID,然后在当前目录查找对应的资源文件,命名规则是:程序名+'.'+语言缩写,语言缩写可以参见附录1。在找不到时才会使用EXE文件中的资源。不过如果系统LCID是English(United
States),用户LCID是Chinese(PRC),由VCL产生的程序就会出现乱码。读者可以自己分析原因。


为VCL程序做多语言版本。只要用Delphi自带的Resource DLL
Wizard再做一个特定语言的资源DLL,原来的程序都不用改。不过很多程序员用其它组件做多语言版本,例如TsiLang 。


MBCS程序虽然也可以做成多语言版本,但它无法在同时显示不同代码页特有的字符,这时就必须使用Unicode程序了。


VS.NET文档中有个多语言资源的例子:SatDLL。它只用Win32
API的例子,却用了VC7项目。我在学习时将它改成了VC6项目,并纠正了它的两个问题:
1、用GetUserDefaultUILanguage读到的是Windows资源版本,不是当前用户设置的代码页。
2、启动时没有使用资源DLL里的菜单。


在我的个人主页(http://www.fmddlmyy.cn)上可以下载修改过的SatDLL。这个程序说明了支持多语言资源的基本思路:将不同语言资源放到不同的DLL中,在程序启动时根据当前Locale装载对应的资源DLL。必要时动态切换资源。为了标记不同语言的资源,可以将它们放到不同的目录中,以LCID作为目录名,例如“2052”、“1033”。当然我们也可以用其它方法联系LCID和资源DLL。


MFC程序可以在App类的InitInstance函数中用AfxSetResourceHandle函数设置资源DLL。在Delphi中动态切换资源可以参考Delphi
Demo目录RichEdit项目的ReInit.pas。在读取当前设定时,建议用GetSystemDefaultLCID函数,因为系统Locale决定ANSI代码页。


3.4 资源和乱码

通过检查可执行文件,我们可以确定VC和Delphi的资源编译器都以Unicode保存字符资源。在VC环境编辑资源时,我们会指定资源的代码页。编译器根据资源的代码页,将其转换到Unicode。


Unicode程序直接使用以Unicode编码保存的资源。MBCS程序需要将Unicode资源先转换回当前ANSI代码页,然后再使用。如果资源中的Unicode字符串不能映射到当前代码页中的字符,就会出现??。


例如Windows的标准对话框也会出现乱码。假设我们使用简体中文Windows,当前Locale是Chinese
(TW),我们的程序是MBCS的,使用标准的打开文件对话框。因为在BIG5中没有“开”这个字,所以“打开”会被显示成“打?”。将程序编译成Unicode版本,就可以避免这个问题。


如果字符不是保存在资源中,而是硬编码在程序中。然后开发者和用户使用不同的代码页,就会导致乱码。假设开发者的Locale是Chinese
(PRC),用户的Locale是English (US),程序中硬编码了字符串“文件”。 Chinese
(PRC)的ANSI代码页是GBK,“文件”的编码“CE C4 BC FE”。English (US)的ANSI代码页是Latin I,用户按照Latin
I编码去解释“CE C4 BC FE”,就会看到“???t”。


回答我前面提过的一个问题:Delphi程序根据用户LCID转换资源中的字符串。如果用户LCID是Chinese
(PRC),系统LCID是English (US)。那么资源中的Unicode字符串会被转换为GBK编码,然后按照Latin
I显示,这时我们看到的就是类似“???t”的东东,不是??。


既然资源是以Unicode保存的,MBCS程序如果不将其转换到ANSI代码页,而用W版本的函数直接显示,就不会产生乱码。例如MFC程序菜单里的中文,在English
(US)的Locale也可以正常显示。不过这取决于各部分代码的具体实现,menu bar控件里的中文在English
(US)的Locale会全部显示成??。

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/68137/viewspace-687394/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/68137/viewspace-687394/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值