unicode,ANSI,UTF-8的故事

考虑到原文有些长,且结构有些杂乱,自己看完后,进行了局部的删减,希望博主能原谅!并在此对博主表示衷心地感谢!

原文见:http://blog.csdn.net/pizi0475/article/details/5429014

修改后的文章如下:

计算机刚发明时,第一套用于存储英文字符的编码为ASCII编码(American
Standard Code for Information Interchange,美国信息互换标准代码),ASCII编码用0~127表示诸如a~z,A~Z等字符的一套编码方案, ,ASCII是一个真正的美国标准;
    当使用计算机的国家开始增多时,人们发现,许多国家的字母在ASCII编码表里没有,因此扩展了ASCII编码,改为0~255,其中从128255这一页的字符集被称"扩展字符集",包括了横线、竖线、交叉等; 
     当计算机被推广至中国等亚洲国家时,人们发现,扩展后的ASCII编码无法表示中国的汉字,聪明的中国人在此基础上制定了符合中国国情的一套编码标准,称为GB2312.

规定:一个小于127的字符的意义与原来相同,但两个大于127的字符连在一起时,就表示一个汉字,前面的一个字节(称之为高字节)从0xA10xF7,后面一个字节(称之为低字节)从0xA10xFE,这样可以组合出7000多个简体汉字,且把数学符号、罗马希腊的字母、日文的假名们都编进去了,连在 ASCII 里本来就有的数字、标点、字母都统统重新编了两个字节长的编码,这就是常说的"全角"字符,而原来在127号以下的那些就
"半角"字符了。然而中国汉字较多,GB2312空间并不够用,于是不再要求低字节一定是127号之后的内码,只要第一个字节是大于127就固定表示这是一个汉字的开始,不管后面跟的是不是扩展字符集里的内容,扩充后的编码方案被称为 GBK 标准,GBK 包括了 GB2312 的所有内容,同时又增加了近20000个新的汉字(包括繁体字)和符号。后来考虑到少数民族的语言文字,于是进一步扩充,GBK扩充后的编码方案为GB18030GB2312GBKGB18030等标准统称为ANSI标准ANSI规定:一个字符用一个字节或多个字节来表示,如一个汉字用两个字节来表示,一个英文字母用一个字节来表示)(备注:注意区分“字符”与“字节”的不同,“字符”是一个与文化相关的符号,如:汉字或韩文等,而“字符”是一个8位的物理存储单元)如:“宁波大学123”在WindowsXP系统的内存中为11个字节,每个汉字占2个字节,而每个英文字母或数字字符占1个字节。

由于不同的国家语言文字的不同,所以各国所订的编码方案也不同,这就带来了诸多操作系统或软件不兼容的难题。ISO决定着手解决这个问题,他们废了所有的地区性编码方案,重新制定了一个全球通用的编码标准,称为UNICODE(宽字节字符集)。(Universal Multiple-Octet Coded Character Set),(UNICODE规定:一个字符必须要用两个字节来表示,如一个汉字用两个字节来表示,而一个英文字母也必须用两个字节来表示)对于ASCII里的那些半角字符,UNICODE 保持其原编码不变,只是将其长度由原来的8位扩展为16位,( eg. ANSI中,’A’=0x43,则对应的UNICODE ‘A’= 0x0043 ) 其他文化和语言的字符则全部重新统一编码。由于"半角"英文符号只需要用到低8位,所以其高8位永远是0,因此这种方案在保存英文文本时会浪费一倍的空间。其中,希腊字母表使用从0x03700x03FF的代码,斯拉夫语使用从0x04000x04FF的代码,美国使用从0x05300x058F的代码,希伯来语使用从0x05900x05FF的代码。中国、日本和韩国的象形文字(总称为CJK)占用了从0x30000x9FFF的代码。

然而不幸的是,UNICODE 被制订时没有考虑与任何一种现有的编码方案保持兼容,这使得GBK GB18030UNICODE 在汉字的内码编排上完全不一样,没有一种简单的算术方法可以把文本内容从UNICODE编码和另一种编码进行转换,这种转换必须通过查表来进行。由于UNICODE 是用两个字节来表示为一个字符,因此共可以组合65535种不同
的字符,如果还不够也没有关系,ISO已经准备了UCS-4方案,说简单了就是四个字节来表示一个字符,这样我们就可以组合出21亿个不同的字符出来(最高位有其他用途),这大概可以用到银河联邦成立那一天吧!

随着计算机网络的兴起,UNICODE 如何在网络上传输也是一个必须考虑的问题,于是面向传输的UTFUCS Transfer Format)标准出现了,UTF8就是一次传输8,而UTF16就是一位传输16位,为了传输的可靠性,从UNICODEUTF时并不是直接的对应,而是要通过一些算法和规则来转换。
     下面讲个小故事。

当你在 windows 的记事本里新建一个文件,输入"联通"两个字之后,保存,关闭,然后再次打开,你会发现这两个字已经消失了,代之的是几个乱码!其实这是因为GB2312编码与UTF8编码产生了编码冲突的原因。
UnicodeUTF-8的编码方式如下:
Unicode编码(16进制)UTF8 字节流(二进制)
0000 - 007F   0xxxxxxx
0080 - 07FF   110xxxxx 10xxxxxx
0800 - FFFF   1110xxxx 10xxxxxx 10xxxxxx

例如""字的Unicode编码是6C496C490800-FFFF之间,所以要用3字节模板:1110xxxx 10xxxxxx 10xxxxxx。将6C49写成二进制是:0110 1100 0100 1001,将这个比特流按三字节模板的分段方法分为0110 110001 001001,依次代替模板中的x,得到:1110-0110 10-110001 10-001001,即E6 B1 89,这就是其UTF8的编码。
而当你新建一个文本文件时,记事本的编码默认是ANSI, 如果你在ANSI的编码输入汉
字,实际就是GB系列的编码方式,在这种编码下,"联通"的内码是:
c1 1100 0001
aa 1010 1010
cd 1100 1101
a8 1010 1000

(备注:此处涉及到了字符内码,解释如下:

字符内码(charcter code)指的是用来代表字符的内码,用户在输入和存储文档时都要使用内码,内码分为
    单字节内码 -- Single-Byte character sets (SBCS),可以支持256个字符编码.
    双字节内码 -- Double-Byte character sets)(DBCS),可以支持65000个字符编码.
前者即为ASCII编码,后者为ANSI
    注意到了吗?第一二个字节、第三四个字节的起始部分的都是"110""10",正好与
UTF8规则里的两字节模板是一致的,于是再次打开记事本时,记事本就误认为这是
一个UTF8编码的文件,让我们把第一个字节的110和第二个字节的10去掉,我们就得
到了"00001 101010",再把各位对齐,补上前导的0,就得到了"0000 0000 0110 1010"
不好意思,这是UNICODE006A,也就是小写的字母"j",而之后的两字节用UTF8
码之后是0368,这个字符什么也不是。这就是只有"联通"两个字的文件没有办法在记
事本里正常显示的原因。 而如果你在"联通"之后多输入几个字,其他的字的编码不见得又恰好是1101开始的字节,这样再次打开时,记事本就不会坚持这是一个UTF8编码的文件,而会用ANSI的方式解读之,这时乱码又不出现了。

UNICODE环境设置
在安装Visual Studio时,在选择VC++时需要加入unicode选项,保证相关的库文件可以拷贝到system32下。


UNICODE编译设置
C/C++, Preprocessor difinitions 去除_MBCS,加_UNICODE,UNICODE
在ProjectSetting/link/output 中设置Entry为
wWinMainCRTStartup
反之为MBCS(ANSI)编译。


Windows CE 本身就是使用UNICODE的一种操作系统,完全不支持ANSI Windows函数
Windows 98 只支持ANSI,只能为ANSI开发应用程序。

Microsoft公司将COM从16位Windows转换成Win32时,公司决定需要字符串的所有COM接口方法都只能接受Unicode字符串。


如何编写Unicode源代码?

Microsoft公司为Unicode设计了WindowsAPI,这样,可以尽量减少代码的影响。实际上,可以编写单个源代码文件,以便使用或者不使用Unicode来对它进行编译。只需要定义两个宏(UNICODE_UNICODE),就可以修改然后重新编译该源文件。

_UNICODE宏用于C运行期头文件,而UNICODE宏则用于Windows头文件。当编译源代码模块时,通常必须同时定义这两个宏。


以下几点在编写MFC程序时较实用:

Windows定义的Unicode数据类型有哪些?
数据类型           说明

WCHAR           Unicode字符  (W为Wide)

PWSTR           指向Unicode字符串的指针
PCWSTR         指向一个恒定的Unicode字符串的指针


对应的ANSI数据类型为CHAR,LPSTR和LPCSTR
ANSI/Unicode通用数据类型为TCHARPTSTR,LPCTSTR


如何区别Unicode和ANSI函数?
字符集           特性                   实例
ANSI            操作函数以str开头
     strcpy
Unicode         操作函数以wcs开头     wcscpy
ANSI/Unicode     操作函数以_tcs开头       _tcscpy(C运行期库)

ANSI/Unicode     操作函数以lstr开头        lstrcpy(Windows函数)

     所有新的和未过时的函数在Windows2000中都同时拥有ANSI和Unicode两个版本。ANSI版本函数结尾以A表示;Unicode版本函数结尾以W表示。Windows作了如下定义:

#ifdef UNICODE
#define CreateWindowEx CreateWindowExW
#else
#define CreateWindowEx CreateWindowExA
#endif  

如何表示Unicode字符串常量?

字符集                       实例

ANSI                                        “string”
Unicode                                    L“string”
ANSI/Unicode               _T(“string”)
TEXT(“string”)

如何编写符合ANSI和Unicode的应用程序?
(1) 将文本串视为字符数组,而不是chars数组或字节数组
;
(2) 将通用数据类型(如TCHAR和PTSTR)用于文本字符和字符串
;
(3) 将显式数据类型(如BYTE和PBYTE)用于字节、字节指针和数据缓存
;
(4) 将TEXT宏用于原义字符和字符串
;
(5) 执行全局性替换(例如用PTSTR替换PSTR)
;
(6) 修改字符串运算问题。例如函数通常希望在字符中传递一个缓存的大小,而不是字节。这意味着不应该传递sizeof(szBuffer),而应该传递(sizeof(szBuffer)/sizeof(TCHAR)。另外,如果需要为字符串分配一个内存块,并且拥有该字符串中的字符数目,那么请记住要按字节来分配内存。这就是说,应该调用 malloc(nCharacters *sizeof(TCHAR)),而不是调用malloc(nCharacters)。


如何在Unicode与ANSI之间转换字符串?
Windows函数MultiByteToWideChar用于将多字节字符串转换成宽字符串;函数WideCharToMultiByte将宽字符串转换成等价的多字节字符串。


衍生标准
UTF-8 / UTF-16
UTF-8标准是Unicode标准的一种变形方式, UTF的全称是:Unicode/UCS Transformation Format,常用的有两种,UTF-8和UTF-16, 其中UTF-8的规则已在本文的上半部分中讲解到,此处略,UTF-16和UTF-8类似,只是每次传递的位数是UTF-8的两倍而已。

 

关于TCHAR

开发ANSI程序时,TCHAR被定义为char;

开发UNICODE 程序时,TCHAR 被定义为wchar_t(16 )
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值