windows 编程 —— 宽字符集 与 Unicode

从ASCII码 到 Unicode



双字节字符集

迄今为止,我们已经看到了256个字符的字符集(ASCII)。但中国、日本和韩国的象形文字符号有大约21,000个。如何容纳这些语言而仍保持和ASCII的某种兼容性呢?

解决方案(如果这个说法正确的话)是双字节字符集(DBCS:double-byte character set)。DBCS从256代码开始,就像ASCII一样。与任何行为良好的代码页一样,最初的128个代码是ASCII。然而,较高的128个代码中的某些总是跟随着第二个字节。这两个字节一起(称作首字节和跟随字节)定义一个字符,通常是一个复杂的象形文字。

虽然中文、日文和韩文共享一些相同的象形文字,但显然这三种语言是不同的,而且经常是同一个象形文字在三种不同的语言中代表三件不同的事。Windows支持四个不同的双字节字符集:代码页932(日文)、936(简体中文)、949(韩语)和950(繁体汉字)。只有为这些国家(地区)生产的Windows版本才支持DBCS。

双字符集问题并不是说字符由两个字节代表。问题在于一些字符(特别是ASCII字符)由1个字节表示。这会引起附加的程序设计问题。例如,字符串中的字符数不能由字符串的字节数决定。必须剖析字符串来决定其长度,而且必须检查每个字节以确定它是否为双字节字符的首字节。如果有一个指向DBCS字符串中间的指针,那么该字符串前一个字符的地址是什么呢?惯用的解决方案是从开始的指针分析该字符串!

Unicode解决方案

我们面临的基本问题是世界上的书写语言不能简单地用256个8位代码表示。以前的解决方案包括代码页和DBCS已被证明是不能满足需要的,而且也是笨拙的。那什么才是真正的解决方案呢?

身为程序写作者,我们经历过这类问题。如果事情太多,用8位数值已经不能表示,那么我们就试更宽的值,例如16位值。而且这很有趣的,正是Unicode被制定的原因。与混乱的256个字符代码映像,以及含有一些1字节代码和一些2字节代码的双字节字符集不同,Unicode是统一的16位系统,这样就允许表示65,536个字符。这对表示所有字符及世界上使用象形文字的语言,包括一系列的数学、符号和货币单位符号的集合来说是充裕的。

明白Unicode和DBCS之间的区别很重要。Unicode使用(特别在C程序设计语言环境里)「宽字符集」。「Unicode中的每个字符都是16位宽而不是8位宽。」在Unicode中,没有单单使用8位数值的意义存在。相比之下,在双字节字符集中我们仍然处理8位数值。有些字节自身定义字符,而某些字节则显示需要和另一个字节共同定义一个字符。

处理DBCS字符串非常杂乱,但是处理Unicode文字则像处理有秩序的文字。您也许会高兴地知道前128个Unicode字符(16位代码从0x0000到0x007F)就是ASCII字符,而接下来的128个Unicode字符(代码从0x0080到0x00FF)是ISO 8859-1对ASCII的扩展。Unicode中不同部分的字符都同样基于现有的标准。这是为了便于转换。希腊字母表使用从0x0370到0x03FF的代码,斯拉夫语使用从0x0400到0x04FF的代码,美国使用从0x0530到0x058F的代码,希伯来语使用从0x0590到0x05FF的代码。中国、日本和韩国的象形文字(总称为CJK)占用了从0x3000到0x9FFF的代码。

Unicode的最大好处是这里只有一个字符集,没有一点含糊。Unicode实际上是个人计算机行业中几乎每个重要公司共同合作的结果,并且它与ISO 10646-1标准中的代码是一一对应的。Unicode的重要参考文献是《The Unicode Standard,Version 2.0》(Addison-Wesley出版社,1996年)。这是一本特别的书,它以其它文件少有的方式显示了世界上书写语言的丰富性和多样性。此外,该书还提供了开发Unicode的基本原理和细节。

Unicode有缺点吗?当然有。Unicode字符串占用的内存是ASCII字符串的两倍。(然而压缩文件有助于极大地减少文件所占的磁盘空间。)但也许最糟的缺点是:人们相对来说还不习惯使用Unicode。身为程序写作者,这就是我们的工作。

宽字符和 C

对C程序写作者来说,16位字符的想法的确让人扫兴。一个char和一个字节同宽是最不能确定的事情之一。没几个程序写作者清楚ANSI/ISO 9899-1990,这是「美国国家标准程序设计语言-C」(也称作「ANSI C」)通过一个称作「宽字符」的概念来支持用多个字节代表一字符的字符集。这些宽字符与常用的字符完美地共存。

ANSI C也支持多字节字符集,例如中文、日文和韩文版本Windows支持的字符集。然而,这些多字节字符集被当成单字节构成的字符串看待,只不过其中一些字符改变了后续字符的含义而已。多字节字符集主要影响C语言程序执行时期链接库函数。相比之下,宽字符比正常字符宽,而且会引起一些编译问题。

宽字符不需要是Unicode。Unicode是一种可能的宽字符集。然而,因为本书的焦点是Windows而不是C执行的理论,所以我将把宽字符和Unicode作为同义语。

Windows 编程中的 "字符” 定义



win32中标准字符定义:

/*标准C字符定义
*/
char c = ‘A‘ ;       //变量c需要1个字节来保存,并将用十六进制数0x41初始化,这是字母A的ASCII代码
char * p ;        //Windows是一个32位操作系统,所以指针变量p需要用4个字节保存
char * p = "Hello!" ; //像前面一样,变量p也需要用4个字节保存。该字符串保存在静态内存中并占用7个字节-6个字节保存字符串,另1个字节保存终止符号0。
char a[10] ;      //编译器为该数组保留了10个字节的储存空间。表达式sizeof(a)将返回10。
char a[] = "Hello!" ; //可使用像下面的语句来初始化一个字符数组,包括末尾的‘\0‘需要7个字节空间

Unicode或者宽字符都没有改变char数据型态在C中的含义。char继续表示1个字节的储存空间,sizeof (char)继续返回1。理论上,C中1个字节可比8位长,但对我们大多数人来说,1个字节(也就是1个char)是8位宽。

typedef unsigned short wchar_t ;  //C中的宽字符基于wchar_t数据型态,它在几个表头文件包括  WCHAR.H  中都有定义
                     //wchar_t数据型态与无符号短整数型态相同,都是16位宽
                    //要定义包含一个宽字符的变量,可使用下面的语句:
wchar_t c = ‘A‘ ;          //C编译器会对该字符进行扩充,使它成为宽字符。

                    //而定义宽字节字符串则:
wchar_t * p = L"Hello!" ;
static wchar_t a[] = L"Hello!" ;
//注意紧接在第一个引号前面的大写字母L(代表「long」)。这将告诉编译器该字符串按宽字符保存-即每个字符占用2个字节。通常,指针变量p要占用4个字节,而字符串变量需要14个字节-每个字符需要2个字节,末尾的0还需要2个字节。

然而要解决的还有一些问题:

1、strlen()在只在接受标准C字符的时候才能正确执行,所有使用 char* 类型的参数的函数都必须重写

2、如何使编写的代码能够同时 支持 使用单字节字符 和 宽字节字符 的系统上运行呢?

对于第一个问题:所有宽字节的函数已被重写,#include<Windows.h> 就可以使用,具体的宽字节函数在 <STRING.H>  <WCHAR.H>有定义比如strlen的宽字节版本为wcslen(wide-character string length:宽字符串长度)

// 宽字节函数 定义于 <STRING.H>  <WCHAR.H>

//strlen函数说明如下:
size_t __cdecl strlen (const char *) ;
//wcslen函数说明如下:
size_t __cdecl wcslen (const wchar_t *) ;    

则
wchar_t  pw[] = L"Hello!" ;
int iLength = wcslen(pw);
int size = sizeof(pw);
// iLength = 6     size = 12

对于第二个问题,

提出的主要原因是 使用Unicode也有缺点。第一点也是最主要的一点是,程序中的每个字符串都将占用两倍的储存空间。此外,您将发现宽字符执行时期链接库中的函数比常规的函数大。出于这个原因,您也许想建立两个版本的程序-一个处理ASCII字符串,另一个处理Unicode字符串。最好的解决办法是维护既能按ASCII编译又能按Unicode编译的单一原始码文件。

虽然只是一小段程序,但由于执行时期链接库函数有不同的名称,您也要定义不同的字符,这将在处理前面有L的字符串文字时遇到麻烦。

一个办法是使用Microsoft Visual C++包含的TCHAR.H表头文件。该表头文件不是ANSI C标准的一部分,因此那里定义的每个函数和宏定义的前面都有一条下划线(_)。TCHAR.H为需要字符串参数的标准执行时期链接库函数提供了一系列的替代名称(例如,_tprintf和_tcslen)。有时这些名称也称为「通用」函数名称,因为它们既可以指向函数的Unicode版也可以指向非Unicode版。

「通用」之所以能实现,是主要通过预编译来实现,通过使用条件编译#ifdef _UNICODE  来 对替代名称的做不同定义来实现

//在头文件 <TCHAR.h> 中定义了替代名称如何被解释成不同的 版本的函数

//如果定义了_UNICODE标识符,那么_TCHAR就是wchar_t:
typedef wchar_t _TCHAR ;  

//如果定义了_UNICODE标识符,那么_TCHAR就是char:
typedef char _TCHAR ;  

还有很多通用的函数都在 TCHAR.H 中声明
时间: 2024-09-30 22:10:35

windows 编程 —— 宽字符集 与 Unicode的相关文章

宽字符集(unicode)操作函数 (转)

字符分类: 宽字符函数 普通C函数 描述 iswalnum() isalnum() 测试字符是否为数字或字母 iswalpha() isalpha() 测试字符是否是字母 iswcntrl() iscntrl() 测试字符是否是控制符 iswdigit() isdigit() 测试字符是否为数字 iswgraph() isgraph() 测试字符是否是可见字符 iswlower() islower() 测试字符是否是小写字符 iswprint() isprint() 测试字符是否是可打印字符 i

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

上一篇我们学习了Windows编程的文本及字体输出,在以上几篇的实例中也出现了一些带有“TEXT”的Windows宏定义,有朋友留言想了解一些ANSI和Unicode编程方面的内容,本章就来了解和学习一些Windows下关于ANSI和Unicode方面的编程基础. 计算机最早在美国诞生,所以最开始都是以英语为作为交互语言,由于只有26个字母,用一个字节(范围-128 ~ 127)表示,这个范围足够表示26个因为字符和一些常用的控制字符,这个就是ASCII编码.因此最早的各种程序设计语言以及使用的

C语言:宽字符集操作函数(unicode编码)

C语言:宽字符集操作函数(unicode编码) 字符分类: 宽字符函数 普通C函数描述 iswalnum() isalnum() 测试字符是否为数字或字母 iswalpha() isalpha() 测试字符是否是字母 iswcntrl() iscntrl() 测试字符是否是控制符 iswdigit() isdigit() 测试字符是否为数字 iswgraph() isgraph() 测试字符是否是可见字符 iswlower() islower() 测试字符是否是小写字符 iswprint() i

走进windows编程的世界-----字符编码

1   字符编码 1.1编码的历史 1.1.1ASCII码 0=127 7位表示 1.1.2ASCII扩展码 0-255 8为表示. 代码页:通过代码也来切换对应的字符(数字表示) 1.1.3双字节字符集DBCS 使用一个或两个字节表示字符. 1.1.4Unicode编码 全部使用2个字节表示字符 内存 硬盘等资源占用变大.对编码支持度大. 字符集 1.2C 语言和编码 1.2.1单字节的字符和字符串 Char  cText = 'A'; Char * pszText ="ABCD"

Win32 Windows编程 三

一.NMAKE 和 Makefile 1.1  NMAKE - 命令解释器, 根据Makefile文件中定义的脚本,完成项目的编译等操作 1.2 Makefile - 定义编译.连接等脚本语言 1.3 Makefile 文件的使用 1.3.1 基本语法规则 window.exe:window.obj //依赖行 cl.exe window.c /c   //命令行 link.exe window.obj user32.lib window.exe 的依赖项是window.obj,如果window

PoEdu - Windows阶段班 【Po学校】Windows编程 Lesson004_003-2 文件操作

001_函数的不同版本 HANDLE : CreateFile()函数返回一个内核对象的句柄 WINAPI : 一种调用约定,调用方式. _In_ 与 _In_opt_ : 本身没有意义,一个说明宏,来标明这个参数的性质. _In_ 说明此参数是"输入型"参数 _In_Opt_ 说明此参数是"输入指针型"参数 _Out_ 说明此参数是"输出型"参数 输出参数要保障:输出型参数,具有可操作的空间 VS2015中,CreateFile()是一个宏:W

Windows 编程中恼人的各种字符以及字符指针类型

在Windows编程中,很容易见到这些数据类型:LPSTR,LPTSTR,LPCTSTR... 像很多童鞋一样,当初在学Windows编程的时候,对着些数据类型真的是丈二和尚,摸不着头脑,长时间不用就会想不起来.所以,下面就对此做一个总结. 首先,先来看一下ASCII编码和UNICODE编码的区别.下面是<Windows 程序设计>一书中对Unicode编码的说明: 简单地说,Unicode扩展自ASCII字符集.在严格的ASCII中,每个字符用7位表示,或者计算机上普遍使用的每字符有8位宽:

【Windows编程】系列第九篇:剪贴板使用

 上一篇我们学习了常见的通用对话框,本篇来了解剪贴板的使用,它常用于复制粘贴功能. 剪贴板是Windows最早就加入的功能,由于该功能非常实用,我们几乎每天都会使用到.通过剪贴板,我们就可以将数据从一个应用程序传递到另一个应用程序,是一种简单的进程间通信. 许多文档处理软件都有复制.剪切.粘贴功能,这些都是用Windows剪贴板实现的,当然我们也可以在我们的程序中实现自己的剪贴板功能,本篇我们就来实现自己的剪贴板.使用剪贴板时,都是先把源数据先传到剪贴板上,再在需要的时候从剪贴板传输到目的处

有一定基础的 C++ 学习者该怎样学习 Windows 编程?

人的心理有个奇异的特性:一项知识一旦学会之后,学习过程中面临的困惑和不解非常快就会忘得干干净净,似乎一切都是自然而然,本来就该这种.因此,关于「怎样入门」这类问题,找顶尖高手来回答,未必能比一个刚入门不久的人来回答要好.就譬如最高票的那个回答,是一个非常精通 Windows 编程的高人回答的,但这种答案能给刚開始学习的人带来多少帮助,我这里想先打一个问号. 前段时间刚辅导了一个学生学会了 Win32 GUI 编程,刚好看到这个问题,顺手就邀请他回答了.并不是是给他布置总结作业,不过希望能从他这里