1.从Txt看起

新建一个Txt文件,输入字符“ab早”,选择菜单另存为,此时出现4个选项,见1

字符编码学习笔记 (一)_第1张图片

1

这是四种不同的编码。


分别以这四种编码来保存文件,并用二进制编辑器查看生成的文件,可以得到如下的结果。

编码方式

用十六进制表示的文件内容

ANSI

141642673.png

Unicode

141723246.png

Unicode big endian

141734222.png

UTF-8

141803104.png

2

  (一)ANSI

ANSI编码应该算是一套很庞大的体系。简体中文GB2312,繁体中文BIG5等都只是ANSI的一个代码页。

什么是代码页呢?打开Windows的控制面板->区域和语言,可以看到如下的选项。

字符编码学习笔记 (一)_第2张图片

3

这里面每一个选项都是一个代码页。

也可以这么说,如果系统语言选择了中文(简体,中国),那么该系统中ANSI就代表GB2312编码。同理,选择中文(繁体,台湾),那么ANSI就代表BIG5编码。

做个小实验验证一下。在简体中文环境下,新建一个Txt文件输入“ab早”,另存为ANSI编码。切换语言环境为繁体中文(前提是装过对应的语言包),系统要求重启,确定。重启之后,打开之前保存的Txt文件。显示出来的内容变成了“ab婌”。‘早’的GB2312编码是D4E7,‘婌’的BIG5编码也是D4E7。这就验证了之前所说的,同样的数据用ANSI编码,在不同的系统语言环境下,被解释成了不同的字符。

另外,不论是ANSI编码的哪个代码页,都是兼容ASCII码的。也就是说,对于ASCII码中的128个字符,在ANSI中也是用一个字节表示。其它字符都是用两个字节表示的,并且这两个字节都是大于0x7F的。

(二)Unicode

Unicode可以编码大部分语言的大部分字符。在网上找资料的时候,有个问题一直在困扰着我。照理说,现在Unicode的编码范围是0-0x10FFFF,这已经是3个字节了,我疑惑的是为什么Unicode字符统一都是两个字节呢?

继续找资料。我得出的结论是此处的Unicode其实是Utf-16。好吧,大胆猜测,接下来还要小心求证。

求证之前,先来说说什么是Utf-8Utf-16Utf-32(下面简称Utf-x)。Unicode只规定了从数字到字符的对应关系,并没有规定具体的实现方法。因此Utf-x其实是Unicode的具体实现,当然也可以称为Unicode的再编码。从UnicodeUtf-x是有具体的映射关系的,而且是有规律的映射关系,可以用一个简单的函数来转换。

开始求证吧。从UnicodeUtf-x的详细映射方法,可点击最下面的链接(Unicode)查看。这里简要说说Utf-16是怎么映射的。Unicode编码共有21位,当高5位为0时,Unicode编码和Utf-16相同,此时在Utf-16编码中,一个字符占2个字节。当高5位不为0时,Unicode编码经过映射变成了4个字节的Utf-16编码,显然这时一个字符占4个字节。

先来验证高5位为0时的情况。之前已经试过,在Txt文件中输入‘ab早’,另存为UNICODE格式,用二进制编辑器查看,发现‘早’的编码是“E965”。在Unicode详细编码表中查找(下有链接),要注意这里存储方式是小尾的,也就是说‘早’的真正编码是65E9

结果我就不贴了,‘早’字确实在编码表的那个位置。

再来验证高5位不为0时的情况。先找个Unicode编码大于0xFFFF的字符(有些范围内的字符显示出来都是方框,估计我的电脑上就没有这种字符)。用来做实验的字符见下图。141854924.png

4

选用编码为0x20001的那个字符(Word上显示不了这个字符,Txt上面倒是可以)。复制这个字符到Txt中,另存为Unicode,再用二进制编辑器打开。结果如下:

141906844.png

5

编码后的长度是4个字节,这个已经对上了,再看看具体的值对不对。算吧!

      1.U= 0x20001

      2.U’= U – 0x10000 = 0x10001 = 0001 0000 0000 0000 0001

      3.Utf-16= 1101 1000 0100 0000 1101 1100 0000 0001 = 0xD840DC01

      4.考虑到以小尾方式存储 Utf-16’ = 0x40D801DC

      5.是不是和上图中的数字对上了啊!

现在大体上已经可以确认Txt中这个所谓的Unicode其实就是Utf-16,或者至少是一个和Utf-16兼容的编码。

最后说说FFFEFEFF。可以看到当将Txt保存为Unicode编码的时候,不论大小尾,在正式字符编码开始之前,都会有FFFEFEFF。这个是用来表示字节序的,FFFE是小尾字节序,FEFF是大尾字节序。

  (三)Unicode big endian

除了字节序采用大尾之外,其它的都和Unicode一样。

(四)UTF-8

UTF-8也是UNICODE编码的一个具体实现方式。UTF-8的特点是变长编码(1~4个字节),对于0x00~0x7F之间的编码与ASCII码完全相同,而汉字一般情况下编码长度是3个字节。那么下面动手求求‘早’字的UTF-8编码是多少吧!

1.U = 0x65E9 = 01100101 1110 1001

2.套用第三个模板

3.Utf-8 = 11100110 1001 0111 1010 1001 = 0xE697A9

   4.对照 2,结果是对的。

此外可以发现,UTF-8格式的文件,在正式编码之前也有个标记,EFBBBF,这个是UTF-8文件头,用来告诉别人接下来的数据是UTF-8编码。



相关资料:

http://baike.baidu.com/link?url=lqme-rGBqK9t9DbwPv5tm-CDPXKaxYYIOMbrvZncYiRWgFEokJlMhJ8E-Yd7AVARfv0TY03-JrmHf_kKLek3Ta  -----  ANSI

http://blog.csdn.net/xiongxiao/article/details/3741731  -----  多种编码

http://baike.baidu.com/link?url=yxnWqjaIlBpMMenb3gmQtX5thFAds4ysiSgE-HS-lS8qwpsYNYrsdRLM8zrEKXou   ----- 代码页

http://baike.baidu.com/link?url=Op9QHQGnv97slgXaB7YpEsEo_GW3KRfm-Nf202xELyCpcKHacc5xKUvlqvB2Hjlu    -----   Unicode

http://zh.wikibooks.org/wiki/Unicode/0000-0FFF     ----- Unicode详细编码表

http://wenku.baidu.com/link?url=3JS_YTGkFlNZjWSuJRJjdsD0qmddmNr8qV-f1ZzJKgWYdlCco4eDMVhSaA6Qswj_Fxdw69FB2WY2UmXeDDqtnaZUr82Edfag94LH_1v8RbO

-----   GB2312编码表

http://wenku.baidu.com/link?url=9_KJEIm1skZsvmXAZIWyKP8hlybcyWtN7WqOHLYxwdsQMvAbaSRD8Gx4fMd3_PLypd6bmnMhgMmUmd3fq55S3Ip6FXF8D81aesZGkiQtOFS

-----   BIG5编码表