c咨询讨论吧 关注:3贴子:36
  • 2回复贴,共1

UNICODE字符与ANSI字符

取消只看楼主收藏回复

ANSI字符集
ANSI字符集是在ASCII字符集的基础上发展来的.它使用8位表示一个字符最大表示256个字符,但是它实际上和ASCII一样只定义了最低的128个标准字符,较高的128个字符为用户自行扩展.微软在此基础上提出了代码页得概念.代码页为不同的国家和地区对ANSI字符集的高128字符进行了扩展,低128字符仍然是标准字符.代码页概念的提出,使得计算机能够描述的字符更多了
虽然基于8位的ANSI字符集加上代码页得概念使得ANSI能够表示更多的字符,能够描述更多的国家和地区的语言,但是在我们东方中国,日本,朝鲜(韩国)我们使用的是象形文字,在我们汉语中汉字的个数至少近5万个,常用的有3000多个,如果用ANSI字符集是无论如何也无法表示出来所有汉字的.正因为如此就出现了另一个字符集
双字节字符集
.双字节字符集(DBCS)双字节字符集低128字符与ANSI标准相同,高128为非标准的,描述其他国家和地区语言符号,象形文字从257个字符开始.第一个字节为一个值为一个128-255之间的数(注:这个数用有符号类型表示就是一个负数)后面跟着第二个字节.这连个字节定义一个字符,就表示一个象形文字,虽然中文,日文,朝鲜文都是象形文字,而且其中有些还是相同的文字,但是显然的这三种语言是不同的.所以在双字节字符集中也是有代码页这个概念的.比如说:代码页932中文,代码页936日文,949朝鲜文,950繁体中文
在双字节字符集中并不是说字符就是用2个字节表示,其中的标准ASCII字符部分是用一个字节表示的,这样的单双字节共存的编码模式就带来了问题.比如说一个包含英文字母和汉字的字符串,字符串中的字符数就不能从字符串的长度上直接计算出来.这需要经过分析其数据才能计算出来.由此我们不难看出双字节字符集也不能解决多种语言字符的问题.
UNICODE字符
很明显的我们面临的问题是世界上的书写语言无法用256个8位代码来表示.即使是双字节字符集能够表示复杂的象形文字,但是这个方法是笨拙的.而且给编程也带来了麻烦.
什么才是更好的解决方案呢?
我们编程的时候都遇到过这个问题,如果我们想描述一个数8位不够用,我们就是用16位去描述.那么同样的字符的问题也可以如此解决,既然8位无法描述所有的书写语言,那么我们为什么不考虑是用16位来描述呢?事实上我们已经在这么做了,这就是所谓的UNICODE字符集.
首先我们应该明白UNICODE字符与双字节字符的区别.在UNICODE中我们使用16位来表示字符,注意所有的字符都是16位表示的.而双字节字符我们还是处理8位的数据(ASCII标准部分是8位的;象形文字部分虽然是16位,实际上我们也是按2个8位去处理).
双字节字符串非常杂乱,但是UNICODE则显得更加的有秩序,因为所有的都是用16位来描述.在这里有必要简单的描述下UNICODE字符集中的内容.UNICODE字符集:前128个字符,也就是从
0x0000-0x007f是标准的ASCII字符,接下来的128个(0x0080-0x00ff)是 ISO-8859-1对ASCII的扩展.希腊字母使用从0x0370-0x3ff的代码,斯拉夫语使用从0x0400-0x4ff的代码,美国使用从
0x530-0x58f的代码,希伯来语使用从0x590-0x5ff的代码.中国,日本,韩国的象形文字(总称CJK)占用了从0x3000-0x9fff的代码.
UNICODE最大的好处就是只有一个字符集,那么它有缺点吗?当然有,UNICODE占用内存时ASCII的2倍,也许更大的问题是目前很多人都不习惯UNICODE.但是作为一个编程人员来说我们有我们的工作.
未来操作系统的发展方向很明显的是UNICODE无疑.UNICODE未来将给我们带来更多的好处.
你的程序在简体中文系统上开发的,能够让它不做任何修改正常的运行在繁体中文系统上吗?不能的话就请使用UNICODE吧.
ANSI 与 UNICODE 维护单一的源代码
我们都知道,如果我们使用传统的方法在简体中文系统上开发一个程序,然后把它拿到繁体中文


IP属地:山东来自手机贴吧1楼2015-06-04 19:16回复
    系统中运行,是会出现乱码的.为什么呢?因为字符集.比如说我们在XP系统中编写程序A,正常我们这个程序使用的字符集是简体中文字符集.把A 移植到繁体中文系统上,就会发现繁体中文上使用的是繁体中文的字符集,而并不存在简体中文的字符集,结果就会发现程序中的字符是乱码.有没有一种办法让我们的程序有跨平台的功能?即使用简体中文开发的程序也可以在繁体中文系统中,正常的显示出简体中文的汉字呢?有办法的.那就是使用UNICODE字符集.
    首先我要先说一下目前NT内核的操作系统,目前我们多数用的是XP,2003,vista,win7
    在这几款主流操作系统中,XP,2003在系统核心上完全支持UNICODE,在用户层是不全面支持的,而vista和win7则是全面支持UNICODE,也就是说我们在XP,2003这样的系统中编写的用户层应用程序默认情况下是非UNICODE版本,如果想编写UNICODE也是可以得,但是需要对开发平台进行设置.比如说VISUAL C++ 6.0它默认的就不是UNICODE如果想开发出UNICODE程序就要进行设置.而在vs 2008中默认的就是UNICODE.这个问题我就不多说了.我们现在关注的这个问题,既然开发环境有UNICODE和非UNICODE两种选择,那么我们编程的时候可能就会出现一个新的问题:我的程序我有时候想让它是UNICODE版本,有时候我想让它是非UNICODE版本,我是不是要维护两种版本的代码啊?
    理论上讲是这样的,两种不同版本的程序需要两种不同的代码.但是微软已经为我们解决了这个问题.我们在查看API的头文件的时候会发现这个问题:有些API函数有2个原型,
    比如说MessageBox这个API,它的定义是这样的:
    WINUSERAPI int MessageBoxA(HWND hWnd,LPCSTR lpText,
    LPCSTR lpCaption,UINT uTYPE);
    WINUSERAPI int MessageBoxW(HWND hWnd,LPCWSTR lpText,
    LPCWSTR lpCaption,UINT uTYPE);
    我们比较两个原型会发现这两个原型只在两个字符串指针的类型上有所不同,A版本的是LPCSTR,而W版本是LPCWSTR(A版本就是ANSI版本,W版本就是UNICODE版本W就是wide缩写意思是” 宽的”).这我们就要看看LPCSTR和LPCWSTR的定义了,我们可以在API的头文件中找到这样的定义:
    对于LPCWSTR:
    typedef unsigned short wchar_t;
    typedef wchar_t WCHAR;
    typedef CONST WCHAR *LPCWSTR, *PCWSTR;
    对于LPCSTR:
    typedef char CHAR;
    typedef CONST CHAR *LPCSTR, *PCSTR;
    可以看出LPCSTR是一个常数型字符指针,而LPCWSTR 是一个常数无符号短整型指针,前者是8位的后者是16位的.前者是用来描述ANSI字符的,后者是用来描述UNICODE字符的.这就是A版和W版得区别.在这里说一下,WINDOWS API 所有的涉及字符串操作的API都有A和W两个版本.
    现在我们知道了,微软已经提供了两个不同版本的API及两个不同长度的字符类型,我们如何在自己的程序中使用唯一的代码来编写适合ANSI和UNICODE两个版本的程序.
    下面请大家看下面这个定义
    #ifdef UNICODE
    #define MessageBox MessageBoxW
    #else
    #define MessageBox MessageBoxA
    #endif // !UNICODE
    这是API头文件中紧跟着MessageBox原型后面的定义.我简单的解释一下:
    如果编译环境设置了UNICODE
    定义 MessageBox 等于 MessageBoxW
    否则 定义MessageBox 等于 MessageBoxA
    11/13
    原来如此.只要有了这个定义在我们程序中就可以直接使用MessageBox这个函数名就可以了,而不必为了在不同版本使用不同的函数名,这样有利于我们维护单一的代码.
    看来函数名我们可以只用一个统一的就可以了,那么参数的类型怎么办呢?
    我们在来看看下面这些定义:
    #ifdef UNICODE // r_winnt
    #ifndef _TCHAR_DEFINED
    typedef WCHAR TCHAR, *PTCHAR;
    typedef WCHAR TBYTE , *PTBYTE ;
    #define _TCHAR_DEFINED
    #endif /* !_TCHAR_DEFINED */
    typedef LPWSTR LPTCH, PTCH;
    typedef LPWSTR PTSTR, LPTSTR;
    typedef LPCWSTR PCTSTR, LPCTSTR;
    typedef LPUWSTR PUTSTR, LPUTSTR;
    typedef LPCUWSTR PCUTSTR, LPCUTSTR;
    typedef LPWSTR LP;
    #define __TEXT(quote) L##quote // r_winnt
    #else /* UNICODE */ // r_winnt
    #ifndef _TCHAR_DEFINED
    typedef char TCHAR, *PTCHAR;
    typedef unsigned char TBYTE , *PTBYTE ;
    #define _TCHAR_DEFINED
    #endif /* !_TCHAR_DEFINED */
    typedef LPSTR LPTCH, PTCH;
    typedef LPSTR PTSTR, LPTSTR, PUTSTR, LPUTSTR;
    typedef LPCSTR PCTSTR, LPCTSTR, PCUTSTR, LPCUTSTR;
    #define __TEXT(quote) quote // r_winnt
    #endif /* UNICODE */ // r_winnt
    #define TEXT(quote) __TEXT(quote) // r_winnt
    如果没有定义_TCHAR_DEFINED
    定义TCHAR为WCHAR, PTCHAR为WCHAR型指针
    定义TBYTE为WCHAR, PTBYTE为WCHAR型指针
    定义_TCHAR_DEFINED
    结束如果!_TCHAR_DEFINED
    定义LPTCH,PTCH为LPWSTR类型;
    定义PTSTR, LPTSTR为LPWSTR类型;
    定义PCTSTR, LPCTSTR为 LPCWSTR类型;
    定义PUTSTR, LPUTSTR为 LPUWSTR类型;
    定义PCUTSTR, LPCUTSTR 为LPCUWSTR类型;
    定义 LP为 LPWSTR类型;
    12/13
    定义 宏__TEXT(quote)为 L##quote
    在前面我们说过##就是连接符它把L和quote连接在一起组成一个新的标识(如:quote为”abcdefg”,那么最终就变成L”abcdefg”)
    否则:
    如果没有定义_TCHAR_DEFINED
    定义TCHAR为char类型,*PTCHAR 为char型指针;
    定义TBYTE 为unsigned char类型, *PTBYTE 为unsigned char型指针;
    定义_TCHAR_DEFINED
    结束如果!_TCHAR_DEFINED
    定义LPTCH, PTCH为LPSTR;
    定义PTSTR, LPTSTR, PUTSTR, LPUTSTR为 LPSTR类型;
    定义PCTSTR, LPCTSTR, PCUTSTR, LPCUTSTR为 LPCSTR类型;
    定义宏 __TEXT(quote) 为 quote
    结束如果 UNICODE
    定义宏TEXT(quote)为 __TEXT(quote)
    从上面这段定义我们可以看出类型的定义中都带有一个T,这个T就是 _TCHAR_DEFINED中的T(我称它为编译时类型), _TCHAR_DEFINED指的是tchar.h头文件,tchar.h不属于SDK它是VC编译器的一部分,但是它确和SDK有着联系,这个头文件中实现了运行时函数与字符操作有关的函数条件编译以及与字符相关变量类型的条件定义.好像有点不容易理解,说白了就是根据我们在编译程序时所选择的ANSI还是UNICODE来决定编译时使用哪种变量类型和那个版本的运行时函数.


    IP属地:山东来自手机贴吧2楼2015-06-04 19:17
    回复
      根据上面的这些定义我们似乎应该知道我们如何在ANSI与UNICODE之间维护唯一的源代码了.看下面的定义
      TCHAR cVariable; //字符型变量
      TCHAR aArray[256]; //字符型数组
      LPTSTR lpstr; //字符串指针
      LPCTSTR lpszName=TEXT(”abcdefg”);//字符串常量
      在上面这些变量定义中我们使用的都是带有T的类型来定义,其好处是无论我们编译程序时是否使用UNICODE这些定义都是可以通过编译的,因为在编译器的实现逻辑中会根据是否定义UNICODE来选择适当的类型的,最终编译器会把这些类型转化为
      CHAR cVariable或者WCHAR cVariable;
      CHAR aArray[256]或者WCHAR aArray[256]
      LPSTR lpstr 或者 LPWSTR
      LPCSTR lpszName=__TEXT(”abcdefg”)或者LPCWSTR lpszName=__TEXT(L”abcdefg”)
      下面给出3种版本的字符串变量,常量定义时的类型,及赋值方法
      ANSI LPSTR str “string”;
      LPCSTR str “string”;
      UNICODE LPWSTR L”string”
      LPCWSTR L”string”;
      编译时 LPTSTR TEXT(“string”);
      LPTWSTR TEXT(“string”);
      一个程序需要UNICODE和ANSI两个不同的版本,如果为此我们开发出两套源代码,想想这有多糟糕.使用编译时类型使我们只维护一套源代码就可以实现两个不同的程序版本,这给我们的工作带来了多大的方便啊


      IP属地:山东来自手机贴吧3楼2015-06-04 19:18
      回复