本文包括2部分内容:“ASCII,Unicode和UTF-8” 和 “Big Endian和Little Endian”。

第1部分 ASCII,Unicode和UTF-8 介绍

1. ASCII码

我们知道,在计算机内部,所有的信息最终都表示为一个二进制的字符串。每一个二进制位(bit)有0和1两种状态,因此八个二进制位就可以组合出256种状态,这被称为一个字节(byte)。也就是说,一个字节一共可以用来表示256种不同的状态,每一个状态对应一个符号,就是256个符号,从0000000到11111111。

上个世纪60年代,美国制定了一套字符编码,对英语字符与二进制位之间的关系,做了统一规定。这被称为ASCII码,一直沿用至今。

ASCII码一共规定了128个字符的编码,比如空格"SPACE"是32(二进制00100000),大写的字母A是65(二进制01000001)。这128个符号(包括32个不能打印出来的控制符号),只占用了一个字节的后面7位,最前面的1位统一规定为0。

2、非ASCII编码

英语用128个符号编码就够了,但是用来表示其他语言,128个符号是不够的。比如,在法语中,字母上方有注音符号,它就无法用ASCII码表示。于是,一些欧洲国家就决定,利用字节中闲置的最高位编入新的符号。比如,法语中的é的编码为130(二进制10000010)。这样一来,这些欧洲国家使用的编码体系,可以表示最多256个符号。

但是,这里又出现了新的问题。不同的国家有不同的字母,因此,哪怕它们都使用256个符号的编码方式,代表的字母却不一样。比如,130在法语编码中代表了é,在希伯来语编码中却代表了字母Gimel (ג),在俄语编码中又会代表另一个符号。但是不管怎样,所有这些编码方式中,0--127表示的符号是一样的,不一样的只是128--255的这一段。

至于亚洲国家的文字,使用的符号就更多了,汉字就多达10万左右。一个字节只能表示256种符号,肯定是不够的,就必须使用多个字节表达一个符号。比如,简体中文常见的编码方式是GB2312,使用两个字节表示一个汉字,所以理论上最多可以表示256x256=65536个符号。

中文编码的问题需要专文讨论,这篇笔记不涉及。这里只指出,虽然都是用多个字节表示一个符号,但是GB类的汉字编码与后文的Unicode和UTF-8是毫无关系的。

3.Unicode

正如上一节所说,世界上存在着多种编码方式,同一个二进制数字可以被解释成不同的符号。因此,要想打开一个文本文件,就必须知道它的编码方式,否则用错误的编码方式解读,就会出现乱码。为什么电子邮件常常出现乱码?就是因为发信人和收信人使用的编码方式不一样。

可以想象,如果有一种编码,将世界上所有的符号都纳入其中。每一个符号都给予一个独一无二的编码,那么乱码问题就会消失。这就是Unicode,就像它的名字都表示的,这是一种所有符号的编码。

Unicode当然是一个很大的集合,现在的规模可以容纳100多万个符号。每个符号的编码都不一样,比如,U+0639表示阿拉伯字母Ain,U+0041表示英语的大写字母A,U+4E25表示汉字"严"。具体的符号对应表,可以查询unicode.org,或者专门的汉字对应表

4. Unicode的问题

需要注意的是,Unicode只是一个符号集,它只规定了符号的二进制代码,却没有规定这个二进制代码应该如何存储。

比如,汉字"严"的unicode是十六进制数4E25,转换成二进制数足足有15位(100111000100101),也就是说这个符号的表示至少需要2个字节。表示其他更大的符号,可能需要3个字节或者4个字节,甚至更多。

这里就有两个严重的问题,第一个问题是,如何才能区别Unicode和ASCII?计算机怎么知道三个字节表示一个符号,而不是分别表示三个符号呢?第二个问题是,我们已经知道,英文字母只用一个字节表示就够了,如果Unicode统一规定,每个符号用三个或四个字节表示,那么每个英文字母前都必然有二到三个字节是0,这对于存储来说是极大的浪费,文本文件的大小会因此大出二三倍,这是无法接受的。

它们造成的结果是:1)出现了Unicode的多种存储方式,也就是说有许多种不同的二进制格式,可以用来表示Unicode。2)Unicode在很长一段时间内无法推广,直到互联网的出现。

5.UTF-8

互联网的普及,强烈要求出现一种统一的编码方式。UTF-8就是在互联网上使用最广的一种Unicode的实现方式。其他实现方式还包括UTF-16(字符用两个字节或四个字节表示)和UTF-32(字符用四个字节表示),不过在互联网上基本不用。重复一遍,这里的关系是,UTF-8是Unicode的实现方式之一。

UTF-8最大的一个特点,就是它是一种变长的编码方式。它可以使用1~4个字节表示一个符号,根据不同的符号而变化字节长度。

UTF-8的编码规则很简单,只有二条:

1)对于单字节的符号,字节的第一位设为0,后面7位为这个符号的unicode码。因此对于英语字母,UTF-8编码和ASCII码是相同的。

2)对于n字节的符号(n>1),第一个字节的前n位都设为1,第n+1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的unicode码。

下表总结了编码规则,字母x表示可用编码的位。

Unicode符号范围          |   UTF-8编码方式
(十六进制)                      | (二进制)
--------------------+---------------------------------------------
0000 0000-0000 007F   | 0xxxxxxx
0000 0080-0000 07FF   | 110xxxxx 10xxxxxx
0000 0800-0000 FFFF  | 1110xxxx 10xxxxxx 10xxxxxx
0001 0000-0010 FFFF  | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

跟据上表,解读UTF-8编码非常简单。如果一个字节的第一位是0,则这个字节单独就是一个字符;如果第一位是1,则连续有多少个1,就表示当前字符占用多少个字节。

下面,还是以汉字"严"为例,演示如何实现UTF-8编码。

已知"严"的unicode是4E25(100111000100101),根据上表,可以发现4E25处在第三行的范围内(0000 0800-0000 FFFF),因此"严"的UTF-8编码需要三个字节,即格式是"1110xxxx 10xxxxxx 10xxxxxx"。然后,从"严"的最后一个二进制位开始,依次从后向前填入格式中的x,多出的位补0。这样就得到了,"严"的UTF-8编码是"11100100 10111000 10100101",转换成十六进制就是E4B8A5。

第2部分 big endian 和 little endian介绍

big endian(大端法)是指低地址存放最高有效字节(MSB),而little endian(小端法)则是低地址存放最低有效字节(LSB)。

通过文字理解可能比较抽象,下面用图像加以说明。下图是“0x12345678在两种字节序中的存储顺序”:

Big Endian

   低地址                                            高地址
----------------------------------------->
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| | | | |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

说明:上面是0x12345678对应big endian的存储方式。

(01) 0x12345678是int整数,它共有4个字节:分别是0x12, 0x34, 0x56, 0x78;其中,0x12是最高有效字节,0x78是最低有效字节。int占4个字节,这个是常识;0x12是十六进制的表示方式,0x12对应的二进制是00010010,正好是8位,也就是1个字节;因此0x12, 0x34, 0x56, 0x78共是4个字节。

(02) big endian是将最高有效字节存储在低地址中,因为就是0x12(最高有效地址),存在低地址;那么,从低往高地址依次存放0x12  -->  0x34 -->  0x56  --> 0x78。也就是上面图像中的存储方式。

Little Endian

   低地址                                            高地址
----------------------------------------->
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| | | | |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

说明:上面是0x12345678对应little endian的存储方式。

(01) 0x12345678是int整数,它共有4个字节:分别是0x12, 0x34, 0x56, 0x78;其中,0x12是最高有效字节,0x78是最低有效字节

(02) little endian是将最低有效字节存储在低地址中,因为就是0x78(最低有效地址),存在低地址;从低往高地址依次存放0x78 --> 0x56  -->  0x34 -->  0x12。也就是上面图像中的存储方式。

big endian 和 little endian判断方式

下面,我们通过示例程序来判断CPU是大端存储还是小端存储。
源码如下(endian.c):

void main() {
int i = 0x12345678;
char* pc = (char*)&i;
if (*pc == 0x12) {
printf("Big Endian\n");
} else if (*pc == 0x78) {
printf("Little Endian\n");
}
}

我在ubuntu12.04系统下,运行的结果是“Little Endian”。


参考文献:字符编码笔记:ASCII,Unicode和UTF-8

更多内容:

01. skywang的博客目录(持续更新中...)

02. Java 集合系列目录(Category) 

字符编码(ASCII,Unicode和UTF-8) 和 大小端的更多相关文章

  1. 字符编码 ASCII,Unicode和UTF-8的关系

    转自:http://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/00143166410626 ...

  2. 字符编码 ASCII unicode UTF-8

    字符串也是一种数据类型,但是,字符串比较特殊的是还有一个编码问题. 因为计算机只能处理数字,如果要处理文本,就必须先把文本转换为数字才能处理.最早的计算机在设计时采用8个比特(bit)作为一个字节(b ...

  3. 彻底搞清楚字符编码: ASCII, ISO_8859, GB2312,UCS, Unicode, Utf-8

    彻底搞清楚字符编码: ASCII, ISO_8859, GB2312,UCS, Unicode, U 1.ASCII: 0-127(128-255未使用),美国标准 2.IS0-8859-1(lati ...

  4. 字符编码 ASCII,Unicode 和 UTF-8 概念扫盲

    今天中午,我突然想搞清楚Unicode和UTF-8之间的关系,于是就开始在网上查资料. 结果,这个问题比我想象的复杂,从午饭后一直看到晚上9点,才算初步搞清楚. 下面就是我的笔记,主要用来整理自己的思 ...

  5. 字符编码 ASCII、Unicode和UTF-8的关系

    摘抄自廖雪峰 教程 字符编码 我们已经讲过了,字符串也是一种数据类型,但是,字符串比较特殊的是还有一个编码问题. 因为计算机只能处理数字,如果要处理文本,就必须先把文本转换为数字才能处理.最早的计算机 ...

  6. 字符编码ASCII,Unicode 和 UTF-8

    一直对编码的概念很模糊,今天抽空突然想了解下,就找到了这个文章,看完真的豁然开朗,必须感谢阮一峰先生. 一.ASCII 码 我们知道,计算机内部,所有信息最终都是一个二进制值.每一个二进制位(bit) ...

  7. Java 字符编码 ASCII、Unicode、UTF-8、代码点和代码单元

    1 ASCII码 统一规定英语字符与二进制位之间的关系.ASCII码一共规定了128个字符的编码.例如,空格“SPACE”是32(二进制00100000),大写字母A是65(二进制01000001). ...

  8. 字符编码(ASCII,Unicode和UTF-8) 和 大小端(zz)

    本文包括2部分内容:“ASCII,Unicode和UTF-8” 和 “Big Endian和Little Endian”. 第1部分 ASCII,Unicode和UTF-8 介绍 1. ASCII码 ...

  9. 字符编码ASCII、Unicode、GB

    计算机的存储都是二进制的,那么我们平时看到的各种字符都需要通过按照一定的格式转换成为二进制才能在被计算机识别与处理.这个过程便成为编码.常见的编码方式有ASCII.Unicode.GB2312等. 1 ...

随机推荐

  1. 了解Browserify

    Browserify是一个Javascript的库,可以用来把多个Module打包到一个文件中,并且能很好地应对Modules之间的依赖关系.而Module是封装了属性和功能的单元,是一个Javasc ...

  2. 如何选择开源许可证&如何修改项目使其符合某种开源许可证

    作者:zyl910 很多文章介绍了详细的解说了各种开源许可证及它们的区别.但是,具体该选择哪一种许可证?如何修改项目使其符合某种开源许可证?就很少见到指导了.于是本文探讨这两个问题. 一.如何选择开源 ...

  3. how to read from __consumer_offsets topic

    来自:http://grokbase.com/t/kafka/users/15bs2r0m83/kafka-0-8-2-1-how-to-read-from-consumer-offsets-topi ...

  4. 用thinkphp将网络上的图片下载到本地服务器

    我用的thinkphp版本是3.2.3,这个版本的跟更早些版本的调用方法不太一样,正确的调用方法是: Demo3Controller.class <?php namespace Home\Con ...

  5. 九宫格抽奖HTML+JS版

    <html> <head> <meta http-equiv="Content-Type" content="text/html; char ...

  6. 使用jQuery页面加载函数启动定时任务

    var timer; $(document).ready(function(){ if (timer == undefined) timer = window.setInterval(showTime ...

  7. 随机抽样一致性算法(RANSAC)示例及源代码

    作者:王先荣 大约在两年前翻译了<随机抽样一致性算法RANSAC>,在文章的最后承诺写该算法的C#示例程序.可惜光阴似箭,转眼许久才写出来,实在抱歉.本文将使用随机抽样一致性算法来来检测直 ...

  8. 同时大量PPPoE连接请求,攻击PPPoE服务器,导致的用户异常掉线故障分析

    前几天接一个客户电话,PPPoE下用户异常掉线,掉线后拨号困难 到达后,我首先查看PPPoE日志,发现一秒钟内有大量PADI包,浪费服务器的SESSION,同时让服务器无法处理其他用户的请求. 打开W ...

  9. Android 模拟器检测

    参考链接:https://github.com/MindMac/HideAndroidEmulator 从多个方面识别模拟器1.用户习惯:联系人数量.短信数量.相册里面照片数量.安装的应用2.从IME ...

  10. 查看iOS模拟器应用的沙箱文件

    iOS 升级到8.3 以后就不能用iFunBox 这样的工具看沙箱里的文件了(非共享的), 而开发时我们的数据库文件又不在共享目录里.关于这个问题,我们可以看模拟器里的沙箱文件, iOS8.0 以后, ...