字符集与字符编码 (charset & encoding)
乱码是个大坑,相信每个人都遇过,而且是个绕不过去的坑。我理解每个程序员都应该写一篇编码相关的博文,梳理自己对这一块的理解,下面是我反复理解多次之后的学习小结。
1、从记事本的不同编码说起:
打开记事本,输入“我我”,保存为ansi编码(其实是gb2312,这也是默认编码)。再分别另存为unicode(其实是utf-16 little endian)、unicodeBigEndian(其实是utf-16 big endian)、utf8,用UltraEdit打开,切换到二进制模式,内容如下:
编码 | 内容 |
ansi | CE D2 | CE D2 |
unicode | FF FE | 11 62 | 11 62 |
unicode big endian | FE FF | 62 11 | 62 11 |
utf-8 | EF BB BF | E6 88 91 | E6 88 91 |
可以看出,“我”在gb2312里的编码是CE D2,在utf-16 little endian里是11 62,在utf-8里是E6 88 91。FF FE是文件头,用来标识这个文件是unicode little endian格式的,同理,EF BB BF标识文件是utf-8格式。所谓的endian,是字节顺序,不同的操作系统,可能小字节在前、也可能大字节在前,既然不一样,就用一个文件头(学名叫BOM,byte order mark,字节顺序标识符)来标识。具体方法是:找一个在unicode里不存在的字符(FF FE)来表示,EF BB BF是FF FE在utf-8里的编码。各种编码的BOM详见下表:
bytes | encoding |
EF BB BF | utf-8 |
FF FE | utf-16 little endian |
FE FF | utf-16 big endian |
FF FE 00 00 | utf-32 little endian |
00 00 FE FF | utf-32 big endian |
2、乱码的各种现象
文本保存在文件里都是字节byte,那这些byte数组是怎么显示为具体的字符呢?以ansi格式的“我”为例,CE D2显示在界面上的步骤是这样的:windows首先将CE D2转换成它内部使用的编码格式unicode,然后按照unicode编码去字体文件中查找字体图像,最后将图像画在窗口的指定位置上。如下:
- 字符首先以某种编码保存在文件中。
- windows将文件中的编码映射为unicode编码。
- windows根据unicode编码去字体文件中查找字体图像,并画在窗口上。
这3步中的每一步错了,都体现为一种典型的乱码。
- 错误1:如果弄错了编码格式,比如将“我”的utf-16编码11 62错存成了gb2312的文件,就会出现乱码。
- 错误2:如果映射到unicode出错,例如出现了unicode里未定义的字符编码,windows就会使用缺省字符,通常是?。比如“我”的gb2312编码CE D2在unicode里尚未使用,所以映射不到任何一个有效的unicode编码。
- 错误3:如果映射到了unicode编码,但在字体文件中找不到对应的字符,windows就会显示字体文件中的缺省图像:空白或方格。
3、各种编码ascii、ansi(gb2312/big5/...)、unicode(utf8/16/32/...)
1) ascii用1个字节(共255个)表示所有英文字符。缺点很明显,就是不够用。对于欧洲那些表音的字母类,就已经捉襟见肘了,再遇到中日韩的表义字符动辄上万,就更不够了。于是ansi想出来填坑,但结果是反而把事情搞麻烦了。其实如果一开始就想到字符不够用的问题,再直接整出个全球统一字符集unicode,就不会有这个大坑了。所以说白了,乱码问题是个历史原因造成的问题(话说软件里那么大坑,哪些不是历史原因造成的!Y2K也是一例,但是谁也没那个远见,所以只能迂回前进了)。
2) 然后,各国都发现ascii不够用,于是各自造出自己的编码来满足需要,中国造出gb2312、台湾造出big5大五码、日本造出shift_jis、其他阿拉伯国家、印度也是类似的。先是IBM弄了代码页(CodePage)、再是微软继承了这一套再加了些自己的定义,整成了ansi,包括所有地区的代码页。通过这些代码页,windows可以实现各地区编码与unicode之间的相互转换。(注:代码页的具体文件为:c:\windows\system32\*.nls文件)代码页是为了兼容已有的程序而存在的,如果我们能强制所有程序都转到unicode,那各地区的ansi编码也就没有存在的必要了,说白了只是发展过程中的中间产物。
3) 最后是unicode。
3.1) 先说说字符集与字体编码的区别。对大多数字符集来说,字符集里的编码 = 字符编码。比如“我”字,在gb2312中的编码是CE D2,在big5中是A7 DA。唯独在Unicode里例外,Unicode使用4个字节/32位(实际只用了31位,最高位必须为0)唯一表示全世界所有可能的字符。unicode的容量为231-1 ≈ 21亿个,截止到2005年只用了10万个,所以应该足够用了。但每个字符4字节,太浪费空间了,于是就产生了各种编码方式,常见的有utf-8、utf-16、utf-32等。这里,unicode是字符集,utf-8/16/32是字符编码。同样是“我”字,在unicode里是00 00 62 11,在utf-8里是E6 88 91,在utf-16里是62 11,在utf-32里才是00 00 62 11。
3.2) unicode到utf8/16/32的转换方式就不详述了。无非又是一些位操作,对理解这个问题的主干无益。有兴趣的可以参看本文最后的引用。
4、对比一下各编码的存储效率
- 存储英文时:utf-8 = ansi > utf-16 > utf-32
- 存储中文时:ansi ≈ utf-16 > utf-8 > utf-32
当然,这里说的是大多数的情况,中英文混杂、多语言混杂、小语种文本都需要具体讨论。
5、参考
- 伐木丁丁鸟鸣嘤嘤:《浅谈文字编码和Unicode(上)》和他做的小工具CodeView:快速查看文本编码
- 阮一峰:《字符编码笔记:ASCII,Unicode和UTF-8》
- 中韩翻译网 金圣镇:《字体编辑用中日韩汉字Unicode编码表》
字符集与字符编码 (charset & encoding)的更多相关文章
- Java应用开发中的字符集与字符编码
事出有因 在向HttpURLConnection的输出流写入内容时,因没有设置charset,导致接收方对数据的验签不一致. URL url = new URL(requestUrl); //打开连接 ...
- 关于Unicode,字符集,字符编码,每个程序员都应该知道的事
关于Unicode,字符集,字符编码,每个程序员都应该知道的事 作者:Jack47 李笑来的文章如何判断一个人是否聪明?中提到: 必要.清晰.且准确的概念,是一切思考的基石.所谓思考,很大程度上,就是 ...
- 刨根究底字符编码之十——Unicode字符集的字符编码方式CEF
Unicode字符集的字符编码方式CEF 一.字符编码方式CEF的选择 1. 由于Unicode字符集非常大,有些字符的编号(码点值)需要两个或两个以上字节来表示,而要对这样的编号进行编码,也必须使用 ...
- 关于Unicode,字符集,字符编码
基本概念 字符[character] 字符代表了字母表中的字符,标点符号和其他的一些符号.在计算机中,文本是由字符组成的. 字符集合[character set] 由一套用于特定用途的字符组成,例如支 ...
- [转]字符集、字符编码、XML中的中文编码
字符集.字符编码.XML中的中文编码 作为程序员的你是不是对于ASCII .UNICODE.GB2321.UTF-7.UTF-8等等不时出现在你面前的这些有着奇怪意义的词感到很讨厌呢,是不是总觉得好象 ...
- 字符集、字符编码、XML中的中文编码
字符集.字符编码.XML中的中文编码 作为程序员的你是不是对于ASCII .UNICODE.GB2321.UTF-7.UTF-8等等不时出现在你面前的这些有着奇怪意义的词感到很讨厌呢,是不是总觉得好象 ...
- 字符集、字符编码、国际化、本地化简要总结(UNICODE/UTF/ASCII/GB2312/GBK/GB18030)
PS:要转载请注明出处,本人版权所有. PS: 这个只是基于<我自己>的理解, 如果和你的原则及想法相冲突,请谅解,勿喷. 环境说明 普通的linux 和 普通的windows. ...
- C# 字符编码类Encoding
在网络通信中,很多情况下都是将字符信息转成字节序列进行传输.将字符序列转为字节序列的过程称为编码.当这些字节传送到接收方,接收方需要逆向将字节序列转为字符序列.这个过程就是解码. 常见编码有ASCII ...
- PythonStudy——字符编码 Character Encoding
测试一下学习字符编码的问题:解决乱码问题 数据 从 硬盘 => 内存 => cpu应用程序打开文本文件的三步骤1.打开应用程序2.将数据加载到内存中3.cpu将内存中的数据直接翻译成字符显 ...
随机推荐
- ES6中的const命令
1.const声明一个只读常量,一旦声明,常量的值就不能改变 1 const PI=3.1415; 2 console.log(PI);//3.1415 3 4 PI=3;//Uncaught T ...
- 巧妙的实现 CSS 斜线(炫酷的小效果)
开本系列,谈谈一些有趣的 CSS 题目,题目类型天马行空,想到什么说什么,不仅为了拓宽一下解决问题的思路,更涉及一些容易忽视的 CSS 细节. 解题不考虑兼容性,题目天马行空,想到什么说什么,如果 ...
- Mybatis关联查询,查询出的记录数量与数据库直接查询不一致,如何解决?
<select id="findUserInfoListForMap" resultMap="BaseResultMap"> SELECT ...
- vim 空格和换行的删除和替换
%s/\s//g %s/\r//g %s/\n//g 把一个很长的一行按空格分为多行 :%s/ +/\r/g简单解释一下:%s :在整个文件范围查找替换/ :分隔符+ :匹配空格,其中“ ”表 ...
- commonJS — 通用方法(for COM)
for COM github: https://github.com/laixiangran/commonJS/blob/master/src/forCOM.js 代码 /** * Created b ...
- phalcon: model 验证数据完整性
The above example performs a validation using the built-in validator “InclusionIn”. It checks the va ...
- android中的通信机制总结
第一种:使用handler来进行通信 handler 大家可以把它想象成主线程(UI线程)的一个子线程,它可以给主线程(UI线程)发送数据从而更新主线程(UI线程)的UI与逻辑,handler ...
- 1到N中1出现的次数
这个问题关键在于好好分析一些样例如: 给定123这个数,你说这个从1到123所有数字中,1出现的次数是多少? 首先我们要分析个位上1出现的次数,我们看看什么情况下个位出现1: 1,11,21,31,4 ...
- stream流批量读取并合并文件
import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.F ...
- HTML5自学笔记[ 10 ]简单的购物车拖拽
用html5拖拽功能实现了一个简单的购物车,样式简陋,得学学画画提高下审美了T^T: <!doctype html> <html> <head> <meta ...