Menu

tlanyan

十里平湖霜满天,寸寸青丝愁华年

文本文件、二进制文件、字符编码

最近被文件搞迷糊了,又和字符编码搞混,不明其中原理导致心烦意乱。昨晚梦到了字符转码等操作,感觉快要走火入魔。今早起床后抓紧看了几篇文章,细细体会,终于消掉文件和编码的疑惑。

文本文件和二进制

最初的疑惑是:文本文件和二进制文件有什么区别?为什么一个能显示内容,另一个的内容经常无法正常显示?

马里兰大学的这篇培训笔记,把两者的区别讲得清楚:文本文件是二进制文件的一种,底层存储也是0和1,与二进制文件没有区别;文本文件可读性和移植性好,但表现字符有限;二进制文件数据存储紧凑,无字符编码限制。在文本文件中,只能存放数字、文字、标点等有限内容;二进制没有字符约束,可随意存储图像、音视频等数据。友好和易容程度上,文本文件大幅胜出。能看懂0和1组合的很少,编辑16进制的数据也是少数人的特殊技巧。

二进制文件的内容,一般无法通过常规编辑器查看,原因是文本编辑器只能显示可见字符(可见字符只有几万个),而二进制中的数据大多是不可显示的。例如一个64位的整数,二进制表示(只)需要八个字节。若以可见字符表示,则需要1-20个字符(从个位数到20位)。常规的文本编辑器,不能分辨整数、浮点数、指针等,只会解析字节。如果字节无对应字符,只好以乱码相待。

编码

说到字符展现,绕不过字符编码。常见的127个ASCII字符,没啥编码好说的,反正几乎所有的编码方式都兼容它。双字节、多字节字符,编码方式和字节序,才是困扰程序员的问题。一个汉字,GBK编码需要两个字节,还要考虑本机的大小端,才能确定存放的最终形式。进行网络通行时,要转换成网络字节序(大端序),接收方才能正常解析。许多开发人员对字符编码不熟悉,通信时遇到乱码问题,调试就很困难。

USC(Universal Multiple Octet Coded Character Set)标准的制定,让开发人员远离混乱的双字节字符集。USC标准里,所有的字符都有了唯一的码点,根据码点就可查到对应字符。USC要求所有的字符都用两个字节(USC-4标准是4个字节),即使是ASCII字符。由于使用了两个字节,可容纳2^16-1(6w+)字符,足够装下各国所有的字符。USC只是一个标准,不对应具体的编码方式。

USC解决了字符集的问题,但是对纯ASCII文件,浪费了50%的空间。接着UTF-8登场,这是一种变长的字符编码方案,既兼容了ASCII码(不浪费ASCII文件的空间,但是浪费汉字文件的空间,哭!),顺便还消除字节序的困:。UTF-8是单字节流,不存在字节序问题,也不需要BOM。目前UTF-8是web通行标准。

对应关系

USC-2与UTF-8的对应关系如下:

十六进制二进制
0000 0000-0000 007F0xxxxxxx
0000 0080-0000 07FF110xxxxx 10xxxxxx
0000 0800-0000 FFFF1110xxxx 10xxxxxx 10xxxxxx
0001 0000-0010 FFFF11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

从编码可以看出,与二进制相比,浪费了很多空间。不过这也没办法:谁让人类的脑袋只能理解可以显示的字符呢?

UTF-8转换规则为: 1. 如果某字节第一位是 0 ,那么判定为 ASCII 字节,除了 0 外余下的 7 位是 ASCII 码,所以 UTF-8 是兼容 ASCII 码的; 2. 如果第一个字节是 1 ,那么连续的几个 “1” 代表从这个字符开始,后面连续的几个字节其实是一个字位,且后面的字节都要以10开头。

了解如上规则,我们的程序便可轻松的处理UTF-8编码的字节流。例如要找出“中”的UTF-8编码,则可以这样处理(注意文件是UTF-8编码):

也可以写出针对UTF-8编码的strlen函数:

了解其原理,和其他语言对接,同样也不是烦恼了。

参考

  1. https://www.cs.umd.edu/class/sum2003/cmsc311/Notes/BitOp/asciiBin.html
  2. http://www.unicode.org/faq/utf_bom.html#BOM
  3. https://my.oschina.net/goal/blog/195749
  4. http://mp.weixin.qq.com/s/2H6LZME03pUFNWBLNl0izw

发表评论

Scroll Up