二、Unicode简介

2.1字符集简史
	最初的ASCII在开发过程中,对确定长度是6,7或者8位产生了争议。最后确定了字符有26个小写26大写加数字等共128个字符,也就是7位长度。很明显这些字符不适用于非美国英语以外的语言,于是增加到了8位,共256个字符。也就是说如果要使用一个字节保存字符,则要128个(2^7)附加字符来补充ASCII。
 
ASCII码( SBCS )
 	ASCII 码使用指定的7 位或8 位二进制数组合来表示128 或256 种可能的字符。标准ASCII 码也叫基础ASCII码,使用7 位二进制数来表示所有的大写和小写字母,数字、标点符号,以及在美式英语中使用的特殊控制字符。
单字节字符集(single-bytecharacter set or SBCS)。在这种编码模式下,所有的字符都只用一个字节表示。ASCII是SBCS。以一个字节表示的0用来标志SBCS字符串的结束。
 
DBCS( MBCS )
	英语用ascii编码就够了,但是世界上每个国家都有自己的文字,ascii肯定是不能满足,如中文,就必须使用两个字节(byte)来代表一个字符。DBCS从256开始,和ASCII一样,最初128个代码是ASCII,较高的128个代码中某些总是跟随第二个字节,这两个字节一起定义一个字符。习惯称为双字节(即DBCS:Double-Byte Character Set),由于Windows里使用的多字节字符绝大部分是两个字节长,所以MBCS常被用DBCS代替。
	但也出现了一个问题:
	双字符集并不是说字符由两个字节代表。一些字符(特别是ASCII字符)由1 个字节表示。这会引起附加的程序设计问题。如,字符串中的字符数不能由字符串的字节数决定。必须剖析字符串来决定其长度,这就要求必须检查每个字节以确定是否为双字节字符的首字节。这显然增加了设计的难度。 
 
UNICODE
	如上面所说的出现的问题,unicode正是为解决此问题而制定的。与混乱的256个字符映像及含有1字节和2字节代码的双字节不同。unicode统一16位,这样就可以表示65536个字符。这对所有目前出现的字符,符号等来说都是足够用的。
	优点:只有一个字符集。
	缺点:统一的16位,占用的内存是ASCII的两倍。

	总结:最初的ASCII只有7位(128个字符),包含美国标准的符号,其后扩展为8位(256字符)。但也不能足够适用于其他国家的语言,于是出现了双字节字符集。但双字节字符集中有字符是占用1字节,有些却是两字节,在判断一个字符是否是8位还是16位的问题上出现了混乱。最后出现了unicode字符集,统一16位,足够使用的同时也解决了混乱问题。
ps:vc6.0默认为DBCS  VS默认unicode
 
2.2宽字符和C语言
下面讨论把unicode和宽字节字符作为同义语。
char数据类型C语言在Win32程序中的标准定义:
	char c=”aaaa”;
	len=strlen(c);
很明显len=4;
当在在unicode下时:
没有改变char数据型态在C 中的含义。 char继续表示1 个字节的储存空间, sizeof (char)继续返回1 。
但C 中的宽字符基于wchar_t数据型态,在包括WCHAR.H的头文件中定义,如:
typedef unsigned short wchar_t ;  //wchar_t为16位
 
同样定义:
wchar_t c=L"aaaa”;
len=strlen(c);
	引号前面的大写字母L(long)。告诉编译器该字符串按宽字符保存-即每个字符占用2个字节。
这时虽然会报错(strlen 该函数应接收char类型的指标,但它现在却接收了一个unsigned short类型的指标),但还是能运行,而此时len=1.   (Ps:这里真不知道作者用的什么编译器,反正我用了vs13和vc6.0都是不能运行的,都是报错,不能转换unsign short 到const char.这里结果为1的理解是,L前缀说明每个字符是2字节,也就是16位,也就是高8位0,低8位是该字符。而strlen得到一个字符串长度并把第1个非0字节做为字符开始计数,但下一个字节是0(表示字符串结束),这样也就只能取得前面一个字符了。)
很明显在使用strlen函数时把c作为16位短整型数据处理了。这也就是两者不同的区别。
 
这样就面临了一个问题,在不同的编码格式下函数不通用?是否要重写这些函数?事实上这些函数也都提供了宽字符的版本。如:
strlen函数的宽字符版是wcslen(wide-characterstring length:宽字符串长度),在STRING.H(包含strlen)和WCHAR.H中均有定义。
strlen函数::
size_t __cdecl strlen (const char *) ;
wcslen函数:
size_t __cdecl wcslen (const wchar_t *) ;
这样如果要得到宽字符串长度可以调用len = wcslen (c) ;
这时len=4。ps:改成宽字节后,字符串的字符长度不改变,组长改变。
 
 
维护单一原始码
使用unicode的缺点是占用空间过大,如果一个程序能处理ASCII字符串,另一个版本处理Unicode字符串。最好的解决办法是维护既能按ASCII编译又能按Unicode编译的单一原始码文件。
我们定义如下:
#define _tcslen wcslen   //如果定义了——UNICODE标识符,且包含了TCHAR.H头文件
#define _tcslen strlen    //如果没有定义unicode
 
typedef wchar_t  TCHAR  //Unicode
typedef char  TCHAR    
 
#define _T(x)  L##x  //Unicode  L添加到宏参数上,如x为aaa,则L##x 为L”aaa”
#define _T(x)  x

此外,还有两个宏与_T相同
#define  _T(x)   _T(x)
#define   _TEXT(x) _T(x)
 
WINNT.H中还定义了一个宏:

#define  _TEXT(quote)    L##quote  //Unicode
#define  _TEXT(quote)    quote     //非Unicode
#define   TEXT(quote)    _TEXT(quote)

T(x)和TEXT(x)作用相同,但无论采用哪一个,都应该在宏内定义字符串文字,如TEXT(“Hello!”)
 
2.3宽字符和Windows
	一个Windows程序包括头文件WINDOWS.H。该文件包括许多其它头文件,如WINDEF.H,定义了许多Windows中使用的基本类型,其也包括WINNT.H。 WINNT.H处理基本的Unicode支持。
WINNT.H包含C的头文件CTYPE.H,是C众多的头文件之一,包括wchar_t的定义。WINNT.H定义了新的数据类型:
typedef char CHAR;
typedef wchar_t  WCHAR;

WINNT.H头文件进而定义了可用做8位字符串指针的六种数据类型和四个可用做const 8位字符串指针的数据型态。
typedef CHAR  *PCHAR,*LPCH,*PCH,*NPSTR,*PSTR;
typedef CONST  CHAR  *LPCCH,*PCCH,*LPCSTR,*PCSTR;

N,L表示near和long。在16位系统中有大小区别,在32位系统中无区别
同理定义了六种可作为16位字符串指针的数据类型和四种可作为const 16位字符串指针的数据类型。
typedef WCHAR * PWCHAR, * LPWCH, * PWCH, *NWPSTR, * LPWSTR, * PWSTR ;
typedef CONST WCHAR * LPCWCH, * PCWCH, *LPCWSTR, * PCWSTR ;

兼容两种字符集的写法
#ifdef UNICODE
typedef WCHAR TCHAR, * PTCHAR ;
typedef LPWSTR LPTCH, PTCH, PTSTR, LPTSTR ;
typedef LPCWSTR LPCTSTR ;
#else
typedef char TCHAR, * PTCHAR ;
typedef LPSTR LPTCH, PTCH, PTSTR, LPTSTR ;
typedef LPCSTR LPCTSTR ;
#endif


上面的各种宏定义可能看起来复杂麻烦,但终究是为了一个目的:编写的程序能兼容ASCII和UNICODE
 
Windows函数调用
WINDOWS.H中函数定义如下:
int WINAPI MessageBox (HWND, LPCSTR,LPCSTR, UINT) ;
函数的第二、三个参数是指向常数字符串的指针。当处理这个函数时,动态链接时其实是调用不同的函数,定义如下:

WINUSERAPI int WINAPI MessageBoxA (HWNDhWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType) ;
WINUSERAPI int WINAPI MessageBoxW (HWNDhWnd, LPCWSTR lpText,LPCWSTR lpCaption, UINT uType) ;
  //定义了宽字符串版本


但我们在调用时可以知道依旧是使用MessageBox函数,关键是在WINUSER.H中完成了,如下:
 

#ifdef UNICODE
#define MessageBox MessageBoxW
#else
#define MessageBox MessageBoxA
#endif

Windows中使用printf.
Windows对标准输入输出没概念,所以在windows中不能使用printf,取而代之的是fprintf
但仍然可以使用sprintf及sprintf系列中的其它函数来显示文字。
sprintf函数声明
int sprint(char* szBuffer,const char*szFormat,…);
考虑以下代码:
printf(“The sum of %i and %i is %i”,1,2,1+2);
等价于:
char buf[100];
sprint(buf, “The sum of %i and %i is %i”,1,2,1+2);
puts(buf);
随着宽字符的发展,sprintf类型函数的增多,使得函数名称变得极为混乱。下面列出了Microsoft  C 执行时期链接库和Windows支持的所有sprintf函数
 

/*---------------------------------------------------------------------------
SCRNSIZE.C -- Displays screen size in a message box
(c) Charles Petzold, 1998
----------------------------------------------------------------------------*/
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
int CDECL MessageBoxPrintf (TCHAR * szCaption, TCHAR * szFormat, ...)
{
	TCHAR szBuffer [1024] ;
	va_list pArgList ;
	va_start (pArgList, szFormat) ;
	_vsntprintf ( szBuffer, sizeof (szBuffer) / sizeof (TCHAR),
		szFormat, pArgList) ;
	va_end (pArgList) ;
	return MessageBox (NULL, szBuffer, szCaption, 0) ;
}
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
					PSTR szCmdLine, int iCmdShow)
{
	int cxScreen, cyScreen ;
	cxScreen = GetSystemMetrics (SM_CXSCREEN) ;
	cyScreen = GetSystemMetrics (SM_CYSCREEN) ;
	MessageBoxPrintf ( TEXT ("ScrnSize"),
		
		TEXT ("The screen is %i pixels wide by %i pixels high."),
		cxScreen, cyScreen) ;
	return 0 ;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值