遇到乱码不怕不怕啦——计算机字符编码详尽讲解

下载一个文档,一打开发现是乱码,不抓狂才怪…… 你们都知道,这都是字符编码闯的祸。ASCII、ANSI、GB18030、Unicode、UTF-8、UTF-8 with BOM、UTF without BOM、UTF-16、UTF-16LE、UTF-16BE…… 一大坨的谁分得清?听说UTF-8就是Unicode,但怎么Windows记事本里的保存选项有UTF-8和Unicode两个选项呀?!究竟各种软件是怎样判断一个文件是什么编码呢?为什么有时候又判断错误呢?让我一一道来。

世界上本没有字符编码。自从有了计算机,我们有了用0和1记录文字的需求,于是字符就有了编码。

== ASCII ==

ASCII编码表示的“Hello GuoKr”(十进制):72 101 108 111 32 71 117 111 75 114

ASCII是最基本的编码,它定义了0~127对应的字符,包括最基本的英文字母、标点符号。它无法表示中文。ASCII编码的文本,每一个字节都是0~127,如果某个字节大于127,那它一定不是ASCII编码。

== GB* / ANSI ==

为了用计算机记录并显示中文,中国人发明了GB系列编码。GB系列编码定义了中文汉字、标点的编码。按照GB系列编码,在一段文本中,如果一个字节是0~127,那么这个字节的含义同ASCII编码,否则,这个字节和下一个字节共同组成汉字(或是GB编码定义的其他字符)。因此,GB系列编码向下兼容ASCII,也就是说,如果一段用GB编码文本里的所有字符都在ASCII中有定义,那么这段编码和ASCII编码完全一样。GB编码早期收录的汉字不足一万个,基本满足日常使用需求,但不包含一些生僻的字,后来在一个个新版本中加进去。最早的GB编码是GB2312,后来有GBK,最新的是GB18030,加入了一些国内少数民族的文字,一些生僻字被编到4个字节,每扩展一次都完全保留之前版本的编码,所以每个新版本都向下兼容。

同样,日文、韩文、世界各国文字都有了它们各自的编码(如果ASCII不能满足使用要求的话)。这些编码都和GB编码相似,兼容ASCII并用两个字节表示一个字。所有这些各国文字编码,微软统称为ANSI 。所以即使知道是ANSI,我们还需要知道这是哪国文字才能解码,因为这些编码都互相冲突。另外,你无法用一段ANSI 编码表示既有汉字、又有韩字的文本。

等等,上面我误导大家了……其实是微软误导大家了,严格来说ANSI 不是字符编码,而是美国一个非营利组织,他们做了很多标准制定工作,包括C语言规范ANSI C,还有各国文字编码对应的“代码页”(code page)。ANSI 规定简体中文GB编码的代码页是936,所以GB编码又叫做ANSI code page 936(按ANSI标准的代码页936),各国编码被统称为ANSI 由来于此——这是个离谱的历史错误,这就像我自己给国内的大学做了个排名,排名的依据是饭堂吃出虫子的概率,然后国内的大学就被统称为黄油猫。不过对于这些乱七八糟互相冲突的多国字符编码,有个统称还是不错的(我了个去还要谢谢微软),下面讨论姑且继续使用ANSI 。

==Unicode / UTF / UCS==

为了解决冲突的问题,促进世界和平,我们有了Unicode(联合码?)——一种将全世界所有语言所有字符都收录在一起、让它们和平共处的编码……哦,等等,Unicode虽是一种字符编码,但严格来说它和GB18030不能相提并论:它只定义了每一个字符对应一个整数(目前包含了十万多个字符,其中0~127和ASCII完全一样),但它没有定义这个整数如何变成字节。当你告诉我这段数据是Unicode编码,啊,不好意思,我还是不知道该怎么解码——因为变成字节流的格式不只一种,它们都叫做“Unicode转换格式”(Unicode Transformation Format,简称为UTF)。

所以这里面就有了一大堆UTF:UTF-8、UTF-8 with BOM、UTF-8 without BOM、UTF-16、UTF-16LE、UTF-16BE…… 还有很少见的UTF-32、,早期还会听说过UCS-2、UCS-4…… _(:з」∠)_

等等,为什么Windows记事本里有个保存选项是Unicode?这个稍后说。

先说最常见的UTF-8:它将一个字符编为1-4个字节,其中一个字节的字符和ASCII 完全一致,所以它也向下兼容ASCII。和ANSI类似,UTF-8第一个字节决定了之后多少个字节是一组好基友。多数汉字在UTF-8里为3个字节,有一些生僻的汉字会编到4字节。

我们迎来第一种不兼容ASCII的编码:UTF-16。UTF-16以每2个字节为一个单元,每个字符由1-2个单元组成,所以每个字符可能是2个字节或者4个字节,包括最常见的英文字母都会编成两个字节。大部分汉字也是2个字节,少部分生僻字为4个字节。UTF-16还有讲究,一个单元中的两个字节的顺序不是唯一的。学过计算机原理的同学知道,计算机中表示一个整数分两种格式:低位在前高位在后,或者反过来。例如用两个字节表示260这个整数,可能是:

低位在前:04 01 (260=4+256*1)

高位在前:01 04 (260=256*1+4)

低位在前的UTF-16叫UTF-16LE,高位在前的叫UTF-16BE。目前绝大部分的计算机系统都使用低位在前的整数格式,所以如果没有声明,UTF-16默认是LE。

早期Unicode收编的字还不多时,两个字节足够表示所有字符,所以有一种固定为两个字节的UTF,叫UCS-2。UTF-16的两个字节部分和UCS-2完全一样,所以UTF-16向下兼容UCS-2。UCS-2同样分LE和BE。而Windows的记事本还有Windows其它地方所谓的Unicode,当代的Windows里其实是UTF-16LE,在Windows XP和更早的版本里是UCS-2LE。微软(又是微软)正是混淆Unicode概念的祸首,微软你这么讨厌你家人知道吗?

此外,UTF-32和UCS-4固定为4个字节一个字符,同样分LE和BE。

还没完,这么多字符编码,软件打开时如何知道是哪个编码?于是不知道谁蹲坑时想出了BOM:在一个文本文件或者一段字符编码前加上几个固定的字节用于识别,这些字节保证不对应任何一个字符,所以软件一读就能验明正身:

EF BB BF - 我是UTF-8

FF FE - 我是UTF-16LE

FE FF - 我是UTF-16BE

(没有BOM,直奔主题)-你猜?

不错,没BOM只能靠猜了。软件读入文件时可以所有编码都试一下,看哪个像。另外,BOM只针对Unicode系列编码,ANSI通通不会有BOM。很显然,没有BOM难免偶然猜错。网上就流传了一个神奇的段子:打开Windows记事本,打入“联通”两个字,保存,关闭,再打开,变成了个黑块。记事本用ANSI(GB18030)保存联通这两个字,刚好这两个字的GB18030编码看起来很像UTF-16,于是就当成UTF-16来打开……

BOM听起来很不错,但实际是个讨厌的设计,因为它和很多协议、规范不兼容,这是题外话。

于是,UTF-8 with BOM、UTF-16 without BOM 你们就懂了。等等,如果不提BOM,究竟有BOM还是没有BOM?—— 又是一个十分纠结的问题,Windows里的软件一般都默认有BOM,而其它系统都默认没有BOM——可能是因为Windows常要兼容ANSI的原因,特别依赖BOM来防止会错意。

==谁是正统==

这么多种编码,用来写文章就罢了,打开个文档看到乱码就退出、换个程序或者换个编码再打开;但写程序时可半点马虎不得,各种程序开发环境对编码支持都不一样,如果编码没搞好,你写好的程序可能在别人计算机上就运行不起来了。如果我开发跨平台的代码,而且要有中文(注释),哪用什么编码好?以下是我所知道各开发环境/编译器支持常见编码情况(所有=ANSI、UTF-8 BOM and no BOM、UTF-16LE BOM and no BOM):

  • Visual Studio:所有,保存默认ANSI 。
  • VC编译器:所有,除了UTF-8 without BOM(直接当成是ANSI )
  • Windows记事本:所有,保存默认ANSI,无法保存无BOM。
  • XCode:只支持 UTF-8 without BOM。
  • GCC:所有
  • vim:所有,保存默认为系统默认编码,一般是UTF-8 without BOM。
  • Eclipse:所有,保存默认不明,Mac下居然是ANSI (我们中出了个叛徒)。

ANSI是无法跨境的,用GB写的文档拿去韩国就果断乱码了。光是简体中文系统,ANSI 也是经常被认错的,Eclipse里经常(不总是)出现打开ANSI 文件是乱码的情况,这是因为ANSI 没有很明显的特征。XCode和Mac的文本编辑器打开ANSI 直接是乱码,因为明确不支持。ANSI 容错性普遍比较差,一个字节错了可能导致后面的字通通挂掉。为了防止Eclipse等发神经,也为免跨国带来麻烦,更为了你自己的数据安全,请远离ANSI。

UTF-16不兼容ASCII,不兼容C语言的字符串处理库函数(因为字节流里有\0),除了Windows爱用,其它系统都痛恨它。BOM和很多协议规范冲突,很多软件都抵制,也是只有Windows常用,而且将其列为正统(作反)。

综上,跨平台开发请使用UTF-8 without BOM,那是最通用的编码,是很多软件系统的默认编码,你在看的网页也用它。它特征明显,除了VC编译器和微软的各种软件外暂时没发现哪个软件会有认错的情况。它还有经过精心设计的容错机制,错一个字节最多只会错一个字符。请手动设置你的开发环境,将默认保存的编码设为UTF-8 without BOM,并将其它编码的文件转换过来,乱码就拜拜啦;记事本等不支持的编辑器,不要让他们摸你的文件。至于VC编译器硬要闹别扭就由它去吧 _(:з」∠)_

(经过测试,VS2010能正常打开UTF-8 without BOM的代码,但可能编译不通过,并报奇怪的编译错误)

为什么程序猿那么喜欢黑微软啊?因为微软那群闷骚的设计师总是和大家不太一样……

写在最后:

在这个机器能听懂人语的年代,我们还要操心字符编码这么低级的事,真是不可思议…… 解决方案这么简单:几家大公司坐一圈,说定一种编码,以后通通用一种编码。而且这种编码已经浮出水面、支持成熟:UTF-8。我很赞赏苹果,他们果断和ANSI还有一大堆不统一的编码一刀两段,所有软件只支持UTF-8,这才是真正对用户负责 —— 而微软到今天还死抱着ANSI不放,在这件小事上就看出他就是如此不舍得过去一点的成就,如此不舍得革自己的命。

转载:http://www.guokr.com/blog/763017/

时间: 2024-11-01 21:03:11

遇到乱码不怕不怕啦——计算机字符编码详尽讲解的相关文章

计算机字符编码详尽讲解

from http://www.guokr.com/blog/763017/ http://blog.csdn.net/stilling2006/article/details/4129700 下载一个文档,一打开发现是乱码,不抓狂才怪…… 你们都知道,这都是字符编码闯的祸.ASCII.ANSI.GB18030.Unicode.UTF-8.UTF-8 with BOM.UTF without BOM.UTF-16.UTF-16LE.UTF-16BE…… 一大坨的谁分得清?听说UTF-8就是Uni

Java 使用Charset类解决读入字符乱码问题和控制输出字符编码

Java中用UTF-16编码字符(见博文Java 正确遍历字符串),Charset类建立了UTF-16编码的字节序列和其它字符编码方式的字节序列之间的映射.当从外面读入表示字符的字节流时,我们可以通过Charset类指定原来字符的编码方式,好让程序可以正确的把字符原来的字节编码转化为Java自己的字节编码:同样,当把字符写出时,我们可以通过Charset指定字符的编码方式.下面举两个简单的例子来说明Charset的使用. public void test1() { Charset charset

计算机字符编码问题

参考链接: 字符编码笔记:ASCII,Unicode和UTF-8 中文编码杂谈 程序员趣味读物:谈谈Unicode编码 http://www.unicode.org/ GB2312简体中文编码表 中日韩Unicode编码表

python学习第十八天计算机字符编码

人类语言和计算机语言二进制怎么沟通,最开始字符编码为ascii码对照表 包括数据和字母,没有汉字,中国自己搞了一套自己的编码 gb2312编码后来发展GBK编码,日本,韩国都,甚至台湾也搞自己的编码,最后国际统一一个编码为unicode 编码 但是unicode编码统一占两个字符,英文占一个字符,中文占两个字符,泰文占3个字符,后来发展 演变 统一 UTF格式 utf-8,这种可长可短的,比较适合所有国家的编码方式. 1,二进制 0,1 二进制 0,1,2,3,4,5,6,7 八进制 0.1.2

转 计算机字符编码中GBK GB2312 GB18030有什么区别

GBK和GB2312 都是16位的, GBK支持简体中文和繁体中文,而GB2312只支持简体中文,GBK里面包含了GB2312,用GBK比较多 GB18030是32位的,它支持简体中文.繁体中文 藏文.蒙文.维吾尔文等主要的少数民族文字, 包含GBK和GB2312 也就是说从GB2312(1980年).GBK(1995年)到GB18030(2000年),这些编码方法是向下兼容的

字符乱码之字符集和字符编码

最近经常会碰到上传客户提供的历史数据上传时csv文件用Excel打开时会有乱码问题,虽然知道知道是字符编码不一致导致的,但其中诸多细节却知之甚少,今日特来理理. 1.为什么会有字符乱码问题?如何解决字符乱码问题 计算机中存储的信息都是二进制数表示的,但是世界上有各种不同的语言文字,所以我们需要一些规则将各种字符转换成二进制后存储在计算机中 即编码,以及如果将存储在计算机中的二进制数解析出来即解码.就如破译电报密码一样如果密码本是错误的,那么密码也是错误.会有字符乱码问题就是因为解码方式是不对的,

计算机在字符编码

计算机字符编码 1.编码发展历史简介   http://www.cnblogs.com/jessonluo/p/4800331.html 使用的字符编码都兼容 ASCII 码 下面主要讲解下 Unicode 编码 ,也就是 万国码 2.Unicode  只是一个编码方案,不是具体的实现 Unicode 编码 只是一种 方案,UTF-8 .UTF-16.UTF-32 才是 Unicode 编码的实现. 原文地址:https://www.cnblogs.com/wfblog/p/10642793.h

python --- 字符编码学习小结

上半年的KPI,是用python做一个测试桩系统,现在系统框架基本也差不多定下来了.里面有用到新学的工厂设计模式以及以及常用的大牛写框架的业务逻辑和python小技巧.发现之前自己写的代码还是面向过程思想的多,基本没有面向对象的思想,近半年看的代码给了很大的触动,我需要升级我的技能了,于是也花了挺多时间在这个KPI学习上,现在先总结下在做这个系统时我所面临到的python的字符编码问题. 字符编码问题,如果处理有问题,可能直接就报错了:如果处理不得当,中文就会显示乱码.这是最初接触字符编码遇到问

精述字符编码(读这篇绝对不够)

带你了解ASCII,Latin1,ANSI,Unicode,UCS-2,UCS-4,UTF-8,UTF-16,UTF-32,GB2312,GB13000,GBK,GB18030,BIG5,BOM,BMP,Littile Endian,Big Endian,内码,外码,CodePage. 1.导语 字符编码(Character Encoding)是计算机显示文本的基础,是每一位IT从业者必知的计算机基础知识点,如同数值在计算中如何存储表示,那么基础,那么重要.然因字符编码历史久远,变更频繁,地域差