十年磨一劍--從程序員到架構師

一个.net程序员,一个企业应用的开发者,喜欢系统架构,数据库,领域驱动,面向对象,表现层技术。关注重用的理论和实践。设计原则:简单,快速,适应变化能力强,表现层灵活多变...

博客园 首页 新随笔 联系 订阅 管理

上篇blog讲了一下unicode等编码的问题﹐不过并没有涉及程序﹐所以这次就用.net来证实一下上次的这些东东。
在证明那些东东之前﹐首先把.net中关于处理encoding,二进制,16进制,byte等相关类别和方法罗列一下。

1.byte与string(那些255以内的整数)的相互转换(各种进制之间的相互转换)
使用System.Convert类别
string to byte
Convert.ToByte(string,base)
base:2表示二进制,8表示八进制,10表示十进制,16表示十六进制(你要输入33,呵呵﹐异常)
这样可以把字符串的(0--255)转成一个byte
Convert.ToByte("01000001",2)转成 65
Convert.ToByte("255",10)转成255
Convert.ToByte("42",16)转成66

同理﹐byte to string也是Convert类
Convert.ToString(byte,base)
同样可以转成相应的进制表示的字符串

通过这两个方法﹐我们要进行2,8,10,16进制的相互转换就容易了

2.char,int,long,boolean等与byte[]之间的相互转换(这些数据在内存中的存储状况)
使用System.BitConverter类别
我们都知道char,int,long等基本类型是以字节形式存在内存中的﹐所以要查看其内存存储方式则直接使用BitConverter.GetBytes()就可以了
然后再使用BitConverter.ToString(byte[])就可以以string方式查看了(如:f9-03表示2个字节)

string是由char组成的﹐只要foreach(char in string)就可以看到string的存储方式了(实验表明﹐string在内存中是以unicode编码存在的,下有示例)

3.各种Encoding之间的转换
使用System.Text中的Encoding相关的类别就可以了
包括Encoding,ASCIIEncoding,UTF8Encoding等,当然也可以通过Encoding.GetEncoding()来获取不同的编码。
然后再通过GetBytes(string)方法﹐就可以获取string的不同编码的byte数组了
通过GetString(byte[])方法﹐就可以把某种编码的byte数组转成字符串.
如"I am 小生,hello world!"的各种bytes编码测试

 

在下面开始之前﹐先摘录一段关于BOM的知识

-----------------------------------------------------------------
UTF的字节序和BOM

UTF-8以字节为编码单元,没有字节序的问题。UTF-16以两个字节为编码单元,在解释一个UTF-16文本前,首先要弄清楚每个编码单元的字节序。例如收到一个“奎”的Unicode编码是594E,“乙”的Unicode编码是4E59。如果我们收到UTF-16字节流“594E”,那么这是“奎”还是“乙”?

Unicode规范中推荐的标记字节顺序的方法是BOM。BOM不是“Bill Of Material”的BOM表,而是Byte Order Mark。BOM是一个有点小聪明的想法:在UCS编码中有一个叫做"ZERO WIDTH NO-BREAK SPACE"的字符,它的编码是FEFF。而FFFE在UCS中是不存在的字符,所以不应该出现在实际传输中。UCS规范建议我们在传输字节流前,先传输字符"ZERO WIDTH NO-BREAK SPACE"。

这样如果接收者收到FEFF,就表明这个字节流是Big-Endian的;如果收到FFFE,就表明这个字节流是Little-Endian的。因此字符"ZERO WIDTH NO-BREAK SPACE"又被称作BOM。

UTF-8不需要BOM来表明字节顺序,但可以用BOM来表明编码方式。字符"ZERO WIDTH NO-BREAK SPACE"的UTF-8编码是EF BB BF。所以如果接收者收到以EF BB BF开头的字节流,就知道这是UTF-8编码了。

Windows就是使用BOM来标记文本文件的编码方式的。


----------------------------------------------------------

好了﹐这些问题解决后﹐我们就来做单纯的文本文件的编码识别﹐读取与写入测试吧。
以windows的notepad为例(其它的文本文件读取软件的原理应该也差不多﹐只是会多一些特殊的判断算法而已)。

notepad默认有四种编码来存储和读取文本文件。分别是﹕
ANSI,Unicode,Unicode-big-endian和UTF-8。
首先来讲ANSI吧﹐这个是windows操作系统在区域与语言块设置的编码(也就是系统默认的编码)﹐因此像繁体操作系统就是big5,而简体操作系统则是GBK。

而Unicode和UTF-8这两种格式相信大家已经有所了解(当然前者是unicode-16)

而Unicode-big-endian是什么意思呢﹐它与Unicode几乎一样﹐只是它把高位放在前面(而后者则刚好相反)
上面的摘录已经有所说明﹐这里再解释一下﹕
如同样是字符"A"﹐在以下几种格式中的存储形式分别是﹕
UTF-16 big-endian : 00 41
UTF-16 little-endian : 41 00
UTF-32 big-endian : 00 00 00 41
UTF-32 little-endian : 41 00 00 00


好了﹐大家想一想﹐文本文件在硬盘中是以字节形式存储的﹐如果不知道文本文件的编码﹐那是无论如何也不能正确读出文本文件显示给用户看的(乱码了只有人才知道﹐程序则认为一切正常)

根据BOM的规则﹐因此在一段字节流开始时﹐如果接收到以下字节﹐则分别表明了该文本文件的编码。
UTF-8: EF BB BF
UTF-16 : FF FE
UTF-16 big-endian: FE FF
UTF-32 little-endian: FF FE 00 00
UTF-32 big-endian: 00 00 FE FF
而如果不是以这个开头﹐那程序则会以ANSI,也就是系统默认编码读取。

所以现在我们来做个测试就可以很清楚地对以上的东东进行验证了。
1.用notepad输入"汉A"这2个字符﹐然后分别保存成ANSI,Unicode,Unicode-big-endian和UTF-8,名字分别取为ansi.txt,unicode.txt,unicode_b.txt,utf8.txt,并且放在c盘根目录下

2.用以下程序进行验证


3.以下是输出格式﹕
ANSI文件格式的字节流﹕
BA-BA-41
10111010 10111010 01000001
Unicode文件格式的字节流﹕
FF-FE-49-6C-41-00
11111111 11111110 01001001 01101100 01000001 00000000
Unicode-big-endian文件格式的字节流﹕
FE-FF-6C-49-00-41
11111110 11111111 01101100 01001001 00000000 01000001
utf-8文件格式的字节流﹕
EF-BB-BF-E6-B1-89-41
11101111 10111011 10111111 11100110 10110001 10001001 01000001

从以上结果可以很容易的看到BABA正是"汉"字的gb2312编码﹐当然我的操作系统是繁体的﹐如果我直接双击打开﹐则可以看到"荦A"﹐这是乱码﹐因为我的系统baba查的是big5﹐而baba的big5码正是"荦"

然而还有其它很多程序﹐像IE呀,它可以使用meta标签来识别文件的编码,xml也是可以通过encoding属性来说明文件的编码的﹐所以这些程序的识别方法和普通的又有些不同罢了。

同样﹐写一个文本文件时﹐先写入这些标记符﹐则也会帮助notepad识别这些文件的编码(当然.net专门提供了一些类别﹐如StreamWriter﹐可以直接存成某种编码的格式)。

至于各种encoding之间的转换﹐我想也不必多说了﹐通过Encoding类的Convert,GetBytes和GetString方法是很容易进行转换的。

原来潜水看别人的文章时发现很简单﹐自己写起来才发现写好一篇blog这么困难(汗...)

posted on 2006-07-13 14:44  Kevin Zou  阅读(5814)  评论(3编辑  收藏  举报