- 浏览: 4709614 次
- 性别:
- 来自: 济南
最新评论
-
wahahachuang8:
GoEasy 实时推送支持IE6-IE11及大多数主流浏览器的 ...
服务器推送技术 -
pdztop:
inffas32.asm(594) inffas32.asm( ...
zlib 在 Visual Studio 2005 下编译失败的解决办法 -
myangle89:
这个方法有效果,但还是绕了一大圈。另外:如果每次这样使用,会造 ...
利用 Spring 与 Log4J 巧妙地进行动态日志配置切换并立即生效 -
lsw521314:
亲,请把用到的包贴出来好么?这版本问题搞得我头大······· ...
lucene MMAnalyzer 实现中文分词 -
guji528:
多命令执行:cmd /k reg delete "H ...
REG Command in Windows XP - Windows XP REG命令的作用和用法
写这篇文章的起因是这么一个问题:我们在使用和安装Windows程序时,有时会看到以“2052”、“1033”这些数字为名的文件夹,这些数字似乎和字符集有关,但它们究竟是什么意思呢?
研究这个问题的同时,又会遇到其它问题。我们会谈到Windows的内部架构、Win32 API的A/W函数、Locale、ANSI代码页、与字符编码有关的编译参数、MBCS和Unicode程序、资源和乱码等,一起经历这段琐碎细节为主,间或乐趣点缀的旅程。
0 Where is Win32 API
Windows程序有用户态和核心态的说法。在32位地址空间中,用户态只能访问0x80000000以下空间(其实只是0x00010000-0x7FFEFFFF),核心态代码可以访问0x80000000以上空间。所有硬件管理都在核心态。用户态代码不能直接使用核心态的任何代码。所谓用户态、核心态其实只是不同的CPU特权级别。在x86 CPU上,用户态处于ring 3,核心态处于ring 0。
从用户态进入核心态的最常用的方法是在寄存器eax填一个功能码,然后执行int 2e。这有点像DOS时代的DOS和BIOS系统调用。在NT架构中这种机制被称作system service。
在核心态提供system service的有两个家伙:ntoskrnl.exe和win32k.sys。ntoskrnl.exe是Windows的大脑,它的上层被称为Executive,下层被称作Kernel。Win32k.sys提供与显示有关的system service。
在用户态一侧,有一个重要的角色叫作ntdll.dll,大多数system service都是它调用的。它封装这些system service,然后提供一个API接口。这个接口被称作native API。 native API的用户是各个子系统(subsystem),包括Win32子系统、OS/2子系统、POSIX子系统。各个子系统为Win32、OS2、POSIX程序提供了运行平台。
ntdll.dll由于提供了平台无关的API接口,所以被看作是NT系统的原生接口,由之得到了“native API”的匪号。其实它的主要工作是将调用传递到核心态。
Win32、OS/2、POSIX,听起来很庞大。其实真正做好的只有Win32子系统。OS2、POSIX都是Console UI,即只有字符界面。提供OS/2子系统,只因为在1988年,NT的主要设计目标就是与OS/2兼容,后来由于Windows 3.0卖得很好,所以设计目标被变更为与Windows兼容。提供POSIX子系统,是为了应付美国政府的一个编号为FIPS 151-2的标准。
Win32子系统的管理员是一个叫作csrss.exe的弟兄,它的全名是:Client/Server Run-Time Subsystem。它刚上任时,本来要分管所有的子系统,但后来POSIX和OS/2都被分别处理了,所以只管了一个Win32。即使这样也很了不起,所有的Win32程序的进程、线程们都要向它登记。
不过Win32程序用得最多的还是Win32子系统的DLL们,最核心的DLL包括:kernel32.dll、User32.dll、Gdi32.dll、Advapi32.dll。这些DLL包装了ntdll.dll的native API。其中Gdi32.dll比较特殊,它与核心态的win32k.sys直接保持联系,以提高NT系统的图形处理能力。Win32子系统的DLL们提供的接口函数在MSDN文档中被详细介绍,它们就是Win32 API。
附录0 Windows的启动
计算机上电后,从BIOS的ROM开始运行。BIOS在做一些初始化后会将硬盘的第一个扇区的数据读入内存,然后将控制权交给它,这段数据被称作Master Boot Record(MBR)。
MBR包含一段启动代码和硬盘的主分区表。这段启动代码扫描主分区表,找到第一个可以启动的分区,然后将这个分区的第一个扇区读入内存并运行。这个扇区被称作引导扇区(boot sector)。
引导扇区的代码具备读文件系统根目录的能力,显然不同的文件系统需要不同的代码。引导扇区会从根目录中读出一个叫作ntldr的文件。顾名思义,这个文件是load NT的主要角色。它的业绩主要包括将CPU从实模式转入保护模式,启动分页机制,处理boot.ini等。
如果boot.ini中有一句:
C:\bootsect.rh="Red Hat Linux"
bootsect.rh的内容是Linux引导扇区,用户又选择了“Red Hat Linux”,ntldr就会将执行Linux的引导扇区,开始Linux的引导。如果用户选择继续使用Windows,ntldr会装载并运行我们前面提到的ntoskrnl.exe。
ntoskrnl.exe会启动会话管理器smss.exe。smss.exe启动csrss.exe和winlogon.exe。smss.exe会永远等待csrss.exe和winlogon.exe返回。如果两者之一异常中止,就会导致系统崩溃。所以病毒们经常以打击csrss.exe为乐。
winlogon.exe负责用户登录,在完成登录后,它会启动注册表HKLM\SOFTWARE\Microsoft\Windows NT\Current Version\Winlogon项下Userinit值指定的程序。该值的缺省数据是userinit.exe。userinit.exe会装载个人设置,让硬盘响个不停,并考验我们的耐性,最后启动注册表同一项下Shell值指定的程序。该值的缺省数据是Explorer.exe。Explorer.exe运行后,我们就会看到熟悉的开始菜单和桌面。
1 Win32 API的A/W函数
要了解Win32子系统的DLL们提供了哪些API,最直接的方法就是用Win32dsm直接查看DLL们的导出表。这时我们会发现Win32 API中带字符串的API一般都有两个版本,例如CreateFileA和CreateFileW。当然也有例外,例如GetProcAddress函数。
A代表ANSI代码页,W是宽字符,即Unicode字符。Windows中的Unicode字符一般指UCS2的UTF16-LE编码。让我们通过几个实例观察A/W版本间的关系。
例1:用WIn32dsm查看gdi32.dll的汇编代码,可以看到TextOutA调用GdiGetCodePage获取当前代码页,再调用MultiByteToWideChar转换输入的字符串,然后调用一个内部函数。而TextOutW直接调用这个内部函数。
例2:用调试器跟踪一个使用了CreateFileA的程序,可以看到:CreateFileA在将输入字符串转换为Unicode后,会调用CreateFileW。假设输入文件名是“测试.txt”,对应的数据就是:“B2 E2 CA D4 2E 74 78 74 00”。
在调试器中可以看到传给CreateFileW的文件名数据是:“4B 6D D5 8B 2E 00 74 00 78 00 74 00 00 00”。 这是"测试.txt"对应的Unicdoe字符串。CreateFileW会接着调用ntdll.dll中的NtCreateFile。顺便看看NtCreateFile的代码:
mov eax, 00000020
lea edx, dword ptr [esp+04]
int 2E
ret 002C
可见这个native API只是简单地调用了核心态提供的0x20号system service。
例3:gdi32.dll中的GetGlyphOutline函数可以获取指定字符的字模。GetGlyphOutlineA和GetGlyphOutlineW函数都会调用同一个内部函数(记作F)。函数F在返回前将通过int 2E调用0x10B1号system service。
GetGlyphOutlineW直接调用函数F。GetGlyphOutlineA在调用函数F前,要依次调用GdiGetCodePage、IsDBCSLeadByteEx和MultiByteToWideChar,将当前代码页的字符编码转换成Unicode编码。
如果我们调用GetGlyphOutlineA时传入“baba”,这是“汉”字的GBK编码,用调试器可以看到传给函数F的字符编码是“6c49”,这是“汉”字的Unicode编码。
从以上例子可见,A版本总会在某处将输入的字符串转换为Unicode字符串,然后和W版本执行相同的代码。在由A/W版本API引出MBCS程序和Unicode程序前,让我们先解释一下Locale和ANSI代码页。
2 Locale和ANSI代码页
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”,就会看到“Îļþ”。
回答我前面提过的一个问题:Delphi程序根据用户LCID转换资源中的字符串。如果用户LCID是Chinese (PRC),系统LCID是English (US)。那么资源中的Unicode字符串会被转换为GBK编码,然后按照Latin I显示,这时我们看到的就是类似“Îļþ”的东东,不是??。
既然资源是以Unicode保存的,MBCS程序如果不将其转换到ANSI代码页,而用W版本的函数直接显示,就不会产生乱码。例如MFC程序菜单里的中文,在English (US)的Locale也可以正常显示。不过这取决于各部分代码的具体实现,menu bar控件里的中文在English (US)的Locale会全部显示成??。
进一步的参考资料
本文的第0节和附录0主要参考了《Inside Windows 2000 Third Edition》,国内出过该书的影印版。DDK文档中有大量Windows内核的信息。用Win32dsm和各种调试器查看Windows系统文件可以获得更直接的信息。
关于Window程序的字符编码,最好的参考资料是winnt.h等SDK的包含文件、VCL、MFC、CRT的源文件。我们不需要阅读它们,只要找到自己感兴趣的信息就可以了,用Source Insight可能方便一些。
本文所谈的不是什么万古不迁的道理,只是别的程序员的一些设定,我们因为需要使用他们的程序,所以有必要了解一些细节。研究问题的方法和兴趣永远比问题本身重要,如一句拉丁俗语所说:res, non verba,实质胜于文字。
尾声
“明月虽有圆缺,但毕竟永恒不灭,人生却如过眼烟云,一去不回,真不知计较为何?”
“蛙声虽是短促,但却是万籁中一个活泼的禅机,也可以说万古如斯,永恒不迁,无奈感受到的,能有几人?”
这是一本武侠书中的对话。在时间的长河中,人生和蛙声一样易逝。说到蛙声,我的20个月的小宝宝在喝汤后,略加酝酿,就会紧闭着嘴巴,发出很像蛙鸣的声音。我们会逗他说:“小青蛙又来了”。小家伙益发得意,不管我的抗议,将连汤带油的小下巴亲热地贴在我的身上。
附录1 一些关于LCID的信息
使用EnumSystemLocales函数可以枚举系统支持的LCID。用GetLocaleInfo可以得到ANSI代码页的ID,再通过GetCPInfoEx可以获得代码页的全称。以下是我在中文Windows XP上读到的内容。
LCID |
国家或地区 |
语言 |
语言缩写 |
ANSI代码页 |
1025 |
沙特阿拉伯 |
阿拉伯语(沙特阿拉伯) |
ARA |
1256 (ANSI - 阿拉伯文) |
1026 |
保加利亚 |
保加利亚语 |
BGR |
1251 (ANSI - 西里尔文) |
1027 |
西班牙 |
加泰隆语 |
CAT |
1252 (ANSI - 拉丁文 I) |
1028 |
台湾 |
中文(台湾) |
CHT |
950 (ANSI/OEM - 繁体中文 Big5) |
1029 |
捷克共和国 |
捷克语 |
CSY |
1250 (ANSI - 中欧) |
1030 |
丹麦 |
丹麦语 |
DAN |
1252 (ANSI - 拉丁文 I) |
1031 |
德国 |
德语(德国) |
DEU |
1252 (ANSI - 拉丁文 I) |
1032 |
希腊 |
希腊语 |
ELL |
1253 (ANSI - 希腊文) |
1033 |
美国 |
英语(美国) |
ENU |
1252 (ANSI - 拉丁文 I) |
1034 |
西班牙 |
西班牙语(传统) |
ESP |
1252 (ANSI - 拉丁文 I) |
1035 |
芬兰 |
芬兰语 |
FIN |
1252 (ANSI - 拉丁文 I) |
1036 |
法国 |
法语(法国) |
FRA |
1252 (ANSI - 拉丁文 I) |
1037 |
以色列 |
希伯来语 |
HEB |
1255 (ANSI - 希伯来文) |
1038 |
匈牙利 |
匈牙利语 |
HUN |
1250 (ANSI - 中欧) |
1039 |
冰岛 |
冰岛语 |
ISL |
1252 (ANSI - 拉丁文 I) |
1040 |
意大利 |
意大利语(意大利) |
ITA |
1252 (ANSI - 拉丁文 I) |
1041 |
日本 |
日语 |
JPN |
932 (ANSI/OEM - 日文 Shift-JIS) |
1042 |
朝鲜 |
朝鲜语 |
KOR |
949 (ANSI/OEM - 韩文) |
1043 |
荷兰 |
荷兰语(荷兰) |
NLD |
1252 (ANSI - 拉丁文 I) |
1044 |
挪威 |
挪威语(伯克梅尔) |
NOR |
1252 (ANSI - 拉丁文 I) |
1045 |
波兰 |
波兰语 |
PLK |
1250 (ANSI - 中欧) |
1046 |
巴西 |
葡萄牙语(巴西) |
PTB |
1252 (ANSI - 拉丁文 I) |
1048 |
罗马尼亚 |
罗马尼亚语 |
ROM |
1250 (ANSI - 中欧) |
1049 |
俄罗斯 |
俄语 |
RUS |
1251 (ANSI - 西里尔文) |
1050 |
克罗地亚 |
克罗地亚语 |
HRV |
1250 (ANSI - 中欧) |
1051 |
斯洛伐克语 |
斯洛伐克语 |
SKY |
1250 (ANSI - 中欧) |
1052 |
阿尔巴尼亚 |
阿尔巴尼亚语 |
SQI |
1250 (ANSI - 中欧) |
1053 |
瑞典 |
瑞典语 |
SVE |
1252 (ANSI - 拉丁文 I) |
1054 |
泰国 |
泰语 |
THA |
874 (ANSI/OEM - 泰文) |
1055 |
土耳其 |
土耳其语 |
TRK |
1254 (ANSI - 土耳其文) |
1056 |
巴基斯坦伊斯兰共和国 |
乌都语 |
URD |
1256 (ANSI - 阿拉伯文) |
1057 |
印度尼西亚 |
印度尼西亚语 |
IND |
1252 (ANSI - 拉丁文 I) |
1058 |
乌克兰 |
乌克兰语 |
UKR |
1251 (ANSI - 西里尔文) |
1059 |
比利时 |
比利时语 |
BEL |
1251 (ANSI - 西里尔文) |
1060 |
斯洛文尼亚 |
斯洛文尼亚语 |
SLV |
1250 (ANSI - 中欧) |
1061 |
爱沙尼亚 |
爱沙尼亚语 |
ETI |
1257 (ANSI - 波罗的海文) |
1062 |
拉脱维亚 |
拉脱维亚语 |
LVI |
1257 (ANSI - 波罗的海文) |
1063 |
立陶宛 |
立陶宛语 |
LTH |
1257 (ANSI - 波罗的海文) |
1065 |
伊朗 |
法斯语 |
FAR |
1256 (ANSI - 阿拉伯文) |
1066 |
越南 |
越南语 |
VIT |
1258 (ANSI/OEM - 越南) |
1067 |
亚美尼亚 |
亚美尼亚语 |
HYE |
936 (ANSI/OEM - 简体中文 GBK) |
1068 |
阿塞拜疆 |
阿塞拜疆语(拉丁文) |
AZE |
1254 (ANSI - 土耳其文) |
1069 |
西班牙 |
巴士克语 |
EUQ |
1252 (ANSI - 拉丁文 I) |
1071 |
前南斯拉夫马其顿共和国 |
马其顿语(FYROM) |
MKI |
1251 (ANSI - 西里尔文) |
1078 |
南非 |
南非语 |
AFK |
1252 (ANSI - 拉丁文 I) |
1079 |
格鲁吉亚 |
格鲁吉亚语 |
KAT |
936 (ANSI/OEM - 简体中文 GBK) |
1080 |
法罗群岛 |
法罗语 |
FOS |
1252 (ANSI - 拉丁文 I) |
1081 |
印度 |
印地语 |
HIN |
936 (ANSI/OEM - 简体中文 GBK) |
1086 |
马来西亚 |
马来语(马来西亚) |
MSL |
1252 (ANSI - 拉丁文 I) |
1087 |
吉尔吉斯坦 |
哈萨克语 |
KKZ |
1251 (ANSI - 西里尔文) |
1088 |
吉尔吉斯斯坦 |
吉尔吉斯语 (西里尔文) |
KYR |
1251 (ANSI - 西里尔文) |
1089 |
肯尼亚 |
斯瓦希里语 |
SWK |
1252 (ANSI - 拉丁文 I) |
1091 |
乌兹别克斯坦 |
乌兹别克语(拉丁文) |
UZB |
1254 (ANSI - 土耳其文) |
1092 |
鞑靼斯坦 |
鞑靼语 |
TTT |
1251 (ANSI - 西里尔文) |
1094 |
印度 |
旁遮普语 |
PAN |
936 (ANSI/OEM - 简体中文 GBK) |
1095 |
印度 |
古吉拉特语 |
GUJ |
936 (ANSI/OEM - 简体中文 GBK) |
1097 |
印度 |
泰米尔语 |
TAM |
936 (ANSI/OEM - 简体中文 GBK) |
1098 |
印度 |
泰卢固语 |
TEL |
936 (ANSI/OEM - 简体中文 GBK) |
1099 |
印度 |
卡纳拉语 |
KAN |
936 (ANSI/OEM - 简体中文 GBK) |
1102 |
印度 |
马拉地语 |
MAR |
936 (ANSI/OEM - 简体中文 GBK) |
1103 |
印度 |
梵文 |
SAN |
936 (ANSI/OEM - 简体中文 GBK) |
1104 |
蒙古 |
蒙古语(西里尔文) |
MON |
1251 (ANSI - 西里尔文) |
1110 |
西班牙 |
加里西亚语 |
GLC |
1252 (ANSI - 拉丁文 I) |
1111 |
印度 |
孔卡尼语 |
KNK |
936 (ANSI/OEM - 简体中文 GBK) |
1114 |
叙利亚 |
叙利亚语 |
SYR |
936 (ANSI/OEM - 简体中文 GBK) |
1125 |
马尔代夫 |
第维埃语 |
DIV |
936 (ANSI/OEM - 简体中文 GBK) |
2049 |
伊拉克 |
阿拉伯语(伊拉克) |
ARI |
1256 (ANSI - 阿拉伯文) |
2052 |
中华人民共和国 |
中文(中国) |
CHS |
936 (ANSI/OEM - 简体中文 GBK) |
2055 |
瑞士 |
德语(瑞士) |
DES |
1252 (ANSI - 拉丁文 I) |
2057 |
英国 |
英语(英国) |
ENG |
1252 (ANSI - 拉丁文 I) |
2058 |
墨西哥 |
西班牙语(墨西哥) |
ESM |
1252 (ANSI - 拉丁文 I) |
2060 |
比利时 |
法语(比利时) |
FRB |
1252 (ANSI - 拉丁文 I) |
2064 |
瑞士 |
意大利语(瑞士) |
ITS |
1252 (ANSI - 拉丁文 I) |
2067 |
比利时 |
荷兰语(比利时) |
NLB |
1252 (ANSI - 拉丁文 I) |
2068 |
挪威 |
挪威语(尼诺斯克) |
NON |
1252 (ANSI - 拉丁文 I) |
2070 |
葡萄牙 |
葡萄牙语(葡萄牙) |
PTG |
1252 (ANSI - 拉丁文 I) |
2074 |
塞尔维亚 |
塞尔维亚语(拉丁文) |
SRL |
1250 (ANSI - 中欧) |
2077 |
芬兰 |
瑞典语(芬兰) |
SVF |
1252 (ANSI - 拉丁文 I) |
2092 |
阿塞拜疆 |
阿塞拜疆语(西里尔文) |
AZE |
1251 (ANSI - 西里尔文) |
2110 |
文莱达鲁萨兰 |
马来语(文莱达鲁萨兰) |
MSB |
1252 (ANSI - 拉丁文 I) |
2115 |
乌兹别克斯坦 |
乌兹别克语(西里尔文) |
UZB |
1251 (ANSI - 西里尔文) |
3073 |
埃及 |
阿拉伯语(埃及) |
ARE |
1256 (ANSI - 阿拉伯文) |
3076 |
香港特别行政区 |
中文(香港特别行政区) |
ZHH |
950 (ANSI/OEM - 繁体中文 Big5) |
3079 |
奥地利 |
德语(奥地利) |
DEA |
1252 (ANSI - 拉丁文 I) |
3081 |
澳大利亚 |
英语(澳大利亚) |
ENA |
1252 (ANSI - 拉丁文 I) |
3082 |
西班牙 |
西班牙语(国际) |
ESN |
1252 (ANSI - 拉丁文 I) |
3084 |
加拿大 |
法语(加拿大) |
FRC |
1252 (ANSI - 拉丁文 I) |
3098 |
塞尔维亚 |
塞尔维亚语(西里尔文) |
SRB |
1251 (ANSI - 西里尔文) |
4097 |
利比亚 |
阿拉伯语(利比亚) |
ARL |
1256 (ANSI - 阿拉伯文) |
4100 |
新加坡 |
中文(新加坡) |
ZHI |
936 (ANSI/OEM - 简体中文 GBK) |
4103 |
卢森堡 |
德语(卢森堡) |
DEL |
1252 (ANSI - 拉丁文 I) |
4105 |
加拿大 |
英语(加拿大) |
ENC |
1252 (ANSI - 拉丁文 I) |
4106 |
危地马拉 |
西班牙语(危地马拉) |
ESG |
1252 (ANSI - 拉丁文 I) |
4108 |
瑞士 |
法语(瑞士) |
FRS |
1252 (ANSI - 拉丁文 I) |
5121 |
阿尔及利亚 |
阿拉伯语(阿尔及利亚) |
ARG |
1256 (ANSI - 阿拉伯文) |
5124 |
澳门特别行政区 |
中文(澳门特别行政区) |
ZHM |
950 (ANSI/OEM - 繁体中文 Big5) |
5127 |
列支敦士登 |
德语(列支敦士登) |
DEC |
1252 (ANSI - 拉丁文 I) |
5129 |
新西兰 |
英语(新西兰) |
ENZ |
1252 (ANSI - 拉丁文 I) |
5130 |
哥斯达黎加 |
西班牙语(哥斯达黎加) |
ESC |
1252 (ANSI - 拉丁文 I) |
5132 |
卢森堡 |
法语(卢森堡) |
FRL |
1252 (ANSI - 拉丁文 I) |
6145 |
摩洛哥 |
阿拉伯语(摩洛哥) |
ARM |
1256 (ANSI - 阿拉伯文) |
6153 |
爱尔兰 |
英语(爱尔兰) |
ENI |
1252 (ANSI - 拉丁文 I) |
6154 |
巴拿马 |
西班牙语(巴拿马) |
ESA |
1252 (ANSI - 拉丁文 I) |
6156 |
摩纳哥公国 |
法语(摩纳哥) |
FRM |
1252 (ANSI - 拉丁文 I) |
7169 |
突尼斯 |
阿拉伯语(突尼斯) |
ART |
1256 (ANSI - 阿拉伯文) |
7177 |
南非 |
英语(南非) |
ENS |
1252 (ANSI - 拉丁文 I) |
7178 |
多米尼加共和国 |
西班牙语(多米尼加共和国) |
ESD |
1252 (ANSI - 拉丁文 I) |
8193 |
阿曼 |
阿拉伯语(阿曼) |
ARO |
1256 (ANSI - 阿拉伯文) |
8201 |
牙买加 |
英语(牙买加) |
ENJ |
1252 (ANSI - 拉丁文 I) |
8202 |
委内瑞拉 |
西班牙语(委内瑞拉) |
ESV |
1252 (ANSI - 拉丁文 I) |
9217 |
也门 |
阿拉伯语(也门) |
ARY |
1256 (ANSI - 阿拉伯文) |
9225 |
加勒比海 |
英语(加勒比海) |
ENB |
1252 (ANSI - 拉丁文 I) |
9226 |
哥伦比亚 |
西班牙语(哥伦比亚) |
ESO |
1252 (ANSI - 拉丁文 I) |
10241 |
叙利亚 |
阿拉伯语(叙利亚) |
ARS |
1256 (ANSI - 阿拉伯文) |
10249 |
伯利兹 |
英语(伯利兹) |
ENL |
1252 (ANSI - 拉丁文 I) |
10250 |
秘鲁 |
西班牙语(秘鲁) |
ESR |
1252 (ANSI - 拉丁文 I) |
11265 |
约旦 |
阿拉伯语(约旦) |
ARJ |
1256 (ANSI - 阿拉伯文) |
11273 |
特立尼达和多巴哥 |
英语(特立尼达) |
ENT |
1252 (ANSI - 拉丁文 I) |
11274 |
阿根廷 |
西班牙语(阿根廷) |
ESS |
1252 (ANSI - 拉丁文 I) |
12289 |
黎巴嫩 |
阿拉伯语(黎巴嫩) |
ARB |
1256 (ANSI - 阿拉伯文) |
12297 |
津巴布韦 |
英语(津巴布韦) |
ENW |
1252 (ANSI - 拉丁文 I) |
12298 |
厄瓜多尔 |
西班牙语(厄瓜多尔) |
ESF |
1252 (ANSI - 拉丁文 I) |
13313 |
科威特 |
阿拉伯语(科威特) |
ARK |
1256 (ANSI - 阿拉伯文) |
13321 |
菲律宾共和国 |
英语(菲律宾) |
ENP |
1252 (ANSI - 拉丁文 I) |
13322 |
智利 |
西班牙语(智利) |
ESL |
1252 (ANSI - 拉丁文 I) |
14337 |
阿联酋 |
阿拉伯语(阿联酋) |
ARU |
1256 (ANSI - 阿拉伯文) |
14346 |
乌拉圭 |
西班牙语(乌拉圭) |
ESY |
1252 (ANSI - 拉丁文 I) |
15361 |
巴林 |
阿拉伯语(巴林) |
ARH |
1256 (ANSI - 阿拉伯文) |
<p
发表评论 |
相关推荐
国际化的字符串编码简介不仅涵盖了基本的字符编码理论,还涉及到在实际开发中如何处理编码问题。了解并熟练掌握这些知识,对于编写跨平台、跨语言的软件至关重要。在进行国际化项目时,对字符串编码的理解和应用能力...
在IT行业中,字符编码是处理文本数据时一个至关重要的概念。不同的编码系统,如ANSI、Unicode和UTF-8,都是为了表示各种语言和符号而设计的。它们各自有不同的特性和适用场景,对于理解和处理字符串长度计算至关重要...
- **映射错误**: 当文本数据中存在编码方案未定义的字符时,系统可能会使用默认字符(通常是问号“?”)进行替代。 - **字体支持**: 如果当前使用的字体不支持某个字符,则系统会显示一个缺省图像(如空白或方格)。...
Unicode编码是一项为文字和符号提供统一编码的标准化技术,旨在解决传统...通过这篇文章,程序员可以了解到字符编码的重要性,以及如何在实际应用中正确地处理Unicode编码,以确保程序能够正确处理各种语言的文本数据。
这对于调试程序、分析数据流或者学习字符编码原理都非常有用。例如,如果你发现一个网页上的汉字显示不正常,可能就是由于编码问题导致的,此时你可以使用内码查看器查看这个汉字的编码,以便找出问题并进行修复。 ...
动态链接库(DLL)是Windows操作系统中的一个重要概念,它允许多个应用程序共享同一段代码,节省内存并简化程序维护。DLL文件包含可执行代码和数据,可以在运行时由多个进程调用。在这个项目中,生成的DLL可能包含了...
在IT行业中,Unicode编码是一种广泛使用的字符编码标准,它能够表示世界上几乎所有的文字和符号,包括汉字。在处理中文字符时,将汉字转化为Unicode编码,特别是在编写正则表达式进行文本处理时,是非常常见且重要的...
在Qt中,`QString`是处理字符串的主要工具,它支持多种编码,并且可以方便地检查其是否为空或判断字符串拼接。 总结,学习Qt在MSVC Windows下的开发涉及多个方面,从环境搭建、项目配置,到理解和应用核心特性,每...
此外,线程和进程的概念也是Windows程序设计中的关键,多线程可以让程序并行执行不同的任务,提高效率。 总的来说,理解和掌握这些VC++常用技术术语,能够帮助开发者编写出高效、规范的代码,更好地驾驭Windows应用...
你可以尝试修改软件的配置文件,或者在系统设置中调整默认的字符编码。 2. **安装兼容字体**:如果是因为系统缺少特定的字体导致的乱码,可以尝试在网上找到并安装缺失的字体。 3. **使用第三方工具**:有些工具如...
在本话题中,我们将深入探讨“查xls表字符转换”这一主题,这通常涉及到在读取或写入Excel文件时,如何处理和转换字符编码问题,以及在进行OLE(Object Linking and Embedding)连接Excel时需要注意的关键点。...
这对于开发者调试程序或理解字符编码有极大的帮助,特别是当涉及到字符处理和文本输入逻辑时。用户可以通过这个工具快速验证和理解键盘输入与计算机内部表示之间的关系。 接着,我们来谈谈调色板。在计算机图形学中...
本文将深入探讨这两种编码系统,并介绍如何使用ThoughtWorks.QRCode.dll库在Windows(WIN)和Web应用程序中生成二维码,以及如何生成条形码,特别是128B类型的条形码。 首先,二维码(Quick Response Code)是一种...
字符处理是任何文字编辑器的基础,它涉及到字符编码、字符显示、文本搜索替换等多个方面。3D Editor中的字符处理模块很可能使用了Unicode编码,确保了对多国语言的支持。同时,为了实现3D效果,编辑器可能对字符的...
在IT行业中,C#是一种广泛使用的编程语言,尤其在开发桌面应用、游戏、Web应用以及Windows服务等领域。本文将深入探讨“C#辅助工具”的核心功能及其在软件开发中的重要性。 C#辅助工具,正如其名,是为C#开发者提供...
在Windows系统中,可以使用内置的EFS(Encrypted File System)进行文件加密,但这里我们介绍的是一个更直接的方法,即通过编写简单的应用程序实现加密。 文件伪装是一种辅助保护措施,它并不改变文件的实际内容,...
4. **应用程序编码**:确保你的应用程序代码(如PHP、Java)与MySQL的字符集保持一致。 此外,如果你在浏览网页时遇到乱码,可能需要调整浏览器的编码设置。文件`下载吧下载说明.htm`和`114啦网址导航.htm`可能是...
文本输出还涉及到字符编码,如ASCII和Unicode(特别是UTF-8)。了解这些编码方式能帮助你处理多语言文本,避免乱码问题。例如,ASCII编码只支持128个基本字符,而Unicode则包含了全世界几乎所有的字符,因此在处理...
在本文中,我们将深入探讨如何使用C++...总的来说,创建一个简单的多线程聊天室涉及到C++多线程编程、网络通信、数据同步以及可能的字符编码选择。通过理解和实践这些概念,你可以构建出一个高效、稳定的实时通信应用。