字符编码:ASCII、Unicode、UTF-8和字节序

目录

ASCII码

Unicode码

UTF-8编码

字节序:Little endian(小端方式) 和 Big endian(大端方式)


ASCII码


在计算机中,所有的数据在存储和运算时都要使用二进制表示。而具体用哪些二进制数表示哪个符号,每个人都可以制定自己的一套规则,这就叫编码。
 
为了让大家互相通信而不造成混乱,就需要使用相同的一套编码规则。
 
ASCII码(美国信息交换标准代码 )就是一套通用的标准编码,主要用于表示英语和其它西欧语言。
 
ASCII码使用一个字节表示一个字符,一个字节(8位二进制)可以表示256个不同的符号,从 0000 0000 到 1111 1111。如ASCII码:
 
二进制
字符
0011 0000
0
0011 0001
1
0100 0001
A
0110 0001
a
英语用128个符号便可以表示所有(0000 0000 ~ 0111 1111),但是用来表示其它语言(如一些西欧语言),128个符号是不够的。于是,一些欧洲国家就决定,利用字节中闲置的最高位编入新的符号(1000 0000 ~ 1111 1111)。这样一来,这些欧洲国家使用的编码体系,可以表示最多256个符号,其中0~127表示的符号是一样的,不一样的只是128 ~ 255的这一段。
 
但是,这又引入了新的问题,同一个最高位为1的二进制(如 128 ~ 255)在不同的国家中表示不同的符号。
 
至于亚洲国家的文字,使用的符号就更多了,汉字就多达10万左右。一个字节只能表示256种符号,肯定是不够的,就必须使用多个字节表示一个符号。
 
 

Unicode码


世界上存在多种语言,如果要表示中文,显然一个字节是不够的,至少需要两个字节,而且还不能和ASCII编码冲突,所以,中国制定了GB2312编码,用来把中文编进去。类似的,日文和韩文等其它国家都制定了各自的编码。
 
为了统一所有文字的编码,Unicode(统一码)应运而生。Unicode把所有语言都统一到一套编码里,每一个符号对应一个独一无二的二进制数,这样就不会再有乱码问题了。
 
对于JAVA/.NET等这些编程语言来说,内置的字符串所使用的字符集已经完全是Unicode。
 
注意:ASCII和Unicode只是一套编码字符集,它只是规定了符号对应的二进制数。
 
 

UTF-8编码


上面提到,Unicode只是一套编码字符集,它只是规定了符号对应的二进制数。也就是Unicode只是字符和二进制数之间的逻辑映射的概念编码,它并没有指明字符对应的二进制数是如何在计算机上存储。
 
至于字符对应的二进制数是如何在计算机上存储(如:一个二进制数使用几个字节存储,这几个字节是如何组合起来的),这就由UTF-8编码来实现。
 
因此,UTF-8 是 Unicode 的一种实现方式
 
UTF-8是一种可变长的编码方式,使用 1 ~ 4个字节来表示Unicode中的任何字符,而且其编码中的第一个字节仍与ASCII相容。
 
UTF-8以字节为单位对Unicode进行编码。从Unicode到UTF-8的编码方式如下:
 
Unicode编码(十六进制)
UTF-8 字节流(二进制)
000000-00007F
0xxxxxxx
000080-0007FF
110xxxxx 10xxxxxx
000800-00FFFF
1110xxxx 10xxxxxx 10xxxxxx
010000-10FFFF
11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
以中文的“汉”为例子:
 
“汉”的Unicode编码的十六进制是 6C 49。
 
6C 49 在 0x0800-0xFFFF 之间,使用3个字节模板:1110xxxx 10xxxxxx 10xxxxxx。
 
将 6C 49 写成二进制是:0110 1100 0100 1001, 用这个比特流依次代替模板中的 x,得到:11100110 10110001 10001001,转成十六进制是 E6 B1 89。
 
 

字节序:Little endian(小端方式) 和 Big endian(大端方式)


“汉”的Unicode编码是2个字节 6C 49,那么 6C 49 在内存或传输中是如何存储的呢?
 
这里先看一下字节序(Byte Order)的概念:
 
字节序是指多字节数据在计算机内存中存储或者网络传输时各字节的存储顺序。
 
字节序有两种方式:
 

1、Little Endian(LE):小端方式,将低序字节存储在起始地址。

6C 49 的 LE 存储方式为:49 6C

2、Big Endian(BE):大端方式,将高序字节存储在起始地址。

6C 49 的 BE 存储方式为:6C 49

不同的场景使用的字节序的方式是不同的:
 
  • Intel、AMD的cpu是 Little Endian;PowerPC 、SPARC和Motorola的cpu是 Big Endian。
  • Internet的网络字节序是 Big Endian。
  • Java的JVM中的字节序是 Big Endian。
Unicode 标准建议用字节序标记(BOM:Byte Order Mark)来区分字节序,即在传输字节流前,先传输 BOM 的标识字符,这个字符叫做“零宽度非换行空格”(zero width no-break space),用 FEFF 表示。如果接收者都到的是FEFF,表示采用大端方式,如果接收的是FFFE,表示采用小端方式。
 
UTF-8 不需要 BOM 来标识字节顺序,但可以用 BOM 来表明编码方式。UTF-8 的 BOM 字符是 EF BB BF,所以如果接收者收到以 EF BB BF 开头的字节流,就知道这是 UTF-8 编码。Windows 是使用 BOM 来标记文本文件的编码方式的,所以,如果文件以 UTF-8 with BOM 编码保存,会在文件开始的地方插入三个不可见的字符 EF BB BF。
 
一般情况下,建议使用 UTF-8,而不是 UTF-8 with BOM。因为BOM字符会带来一些意想不到的问题,如:网页中的空白或间隔、乱码等。
 
一般在 Windows 中,才使用 UTF-8 with BOM 编码。如果一个CSV文件使用 UTF-8 编码,用 Excel 打开 CSV 文件时就会出现乱码,因为 Windows 是使用 BOM 来标记文本文件的编码方式的,此时CSV中缺少BOM字符,Excel 就无法识别到正确的编码方式。解决方法就是使用 UTF-8 with BOM 编码,这样,Excel 就会根据 BOM 字符(EF BB BF)识别到这是 UTF-8 编码。
 
所以,对于要下载或生成 CSV 文件的场景下,要先 write BOM。代码如下:
 
outputStream.write(new byte[]{ (byte)0xEF, (byte)0xBB, (byte)0xBF });
outputStream.write(content.toString().getBytes("UTF-8"));

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值