浅谈编码集

1、名词解释java

在聊编码集以前,咱们先来了解一些名词解释:数组

字符集:所谓字符编码就是一个系统支持的全部抽象字符的集合,也就是说咱们日常使用的文字,标点符号,图形符号等都是字符集。ide

咱们知道,计算机没法识别咱们平时说的文字,只能识别二进制的数字系统,那么就须要一套规则,将咱们所说的字符转换为数字系统,那么这种操做,就是字符编码。官方解释以下:编码

字符编码:将符号转换为计算机能够接受的数字系统的规则。spa

理解了以上两个概念,接下来的两个概念就很容易理解了:
code

编码:按照某种字符编码将字符存储在计算机中blog

解码:将存储在计算机中的二进制数解析出来utf-8

2、编码集的发展史
ASCII编码
ci

ASCII使用8个bit表示一个字节,共有128个字符,编码范围是0-127unicode

众所周知,计算机最早使用是在美国,ASCII编码做为美国“元老级编码”,被称做“美国信息交换标准代码”。

ASCII字符集主要包括两部分,分别是:控制字符可显示字符

控制字符:主要包括回车键、退格键、换行键等

可显示字符:主要包括英文大小写、阿拉伯数字和西文符号等

ISO-8859-1

在ASCII推出后,计算机也在迅猛发展,当西欧等国家也开始使用计算机时,发现,ASCII虽然在显示英文方面作的很好,可是西欧国家的一些语言是没法显示的,因此,他们就对ASCII进行拓展。他们将新的符号填入128-255,将编码范围从0-127拓展成为0-255,共256个字符。相较于ASCII,ISO-8859-1加入了西欧语言,同时还加入了横线、竖线、交叉等符号。

GB系列的诞生

随着计算机的普及,咱们国家也开始使用计算机,可是如何显示中文就成了一个大难题。

使用ASCII对中文进行编码和解码(以JAVA代码为例):

String chineseStr = "哈哈";
//编码
byte[] ascii = chineseStr.getBytes("ASCII");
//解码
String asciiStr = new String(ascii,"ASCII");
System.out.println("使用ASCII编码显示中文"+asciiStr);

运行结果不出所料,中文变成了“??”,可见,ASCII没法知足咱们对中文的须要,但是已经没有可用的字节给咱们用了,这可难不倒咱们中国的劳动人民。那咱们是怎么作的呢?

咱们把127号以后的全部符号(即ASCIIM拓展码)取消掉,而且规定:一个小于127的字符,意义与原来相同,两个大于127的字符连在一块儿表示一个汉字

就这样,咱们组合出7000多个经常使用简体汉字,还包括数字符号、罗马希腊字母、日文假名们。这就是咱们常说的GB2312编码

可是,中国的汉字实在是太多了,还有繁体字也没有编进GB2312中,同时,在GB2312推出后又增长了许多简体字,这些汉字仍是没法显示。

还好,GB2312并无把全部的码位都用完。可是当咱们把剩下的码位填满以后,发现仍是有不少汉字没法编入,咱们天朝的专家又说了,再也不要求第二个字节也是127号之后的字符,只要第一个字节大于127就表示一个汉字的开始

此次的编码,增长了进20000个汉字(包括繁体字)和符号,咱们把这中编码成为GBK编码集。GBK编码包括了GB2312的全部内容。

那么问题来了,以前使用ASCII编码的软件能够在GB系列环境下继续运行吗?

使用ASCII编码,使用GBK解码:

String englishStr = "hello world";
//编码
byte[] ascii = englishStr.getBytes("ASCII");
//解码
String gbkStr = new String(ascii,"GBK");
System.out.println("使用ASCII编码,使用GBK解码:"+gbkStr);

运行结果:没有发生乱码

使用ASCII编码,使用GBK解码:hello world

可见,GB系列解决了显示中文的问题,同时还保证了ASCII遗留软件还能够继续运行

unicode编码

在中国推出本身的GB系列编码的时候,其余国家也都推出了属于本身语言的编码集,各个国家的软件没法作到互通,由于当编码集不一样时,乱码问题就会出现。

终于有一个叫ISO的国际组织实在是看不下去了,他们推出了一个新的编码:unicode编码

unicode编码是一个很大的编码,它废除了全部地区性的编码方案,从新规定了编码方式,包括了地球上全部文化、全部字母和符号。

它要求:

ASCII里的字符保持原编码不变,只是将其长度由原来的8位拓展成了16位;

其余文化语言的字符则所有统一从新编码,一样也是16位

咱们来看一下unicode显示各国语言的效果如何:

String testStr = "abc哈哈하하あはは";
//编码
byte[] unicode = testStr.getBytes("unicode");
//解码
String unicodeStr = new String(unicode,"unicode");
System.out.println("使用UNICODE编码和解码:"+unicodeStr);

运行结果:未发生乱码

使用UNICODE编码和解码:abc哈哈하하あはは

咱们刚才说到,unicode在处理英文时,将其长度拓展为16位,那么也就是说,在处理同等长度的纯英文字符串时,Unicode要使用两倍的空间来存储,为了能更直观地表达意思,使用代码来向你们说明:

String englishStr = "helloworld" ;
//使用ASCII编码
byte[] ascii = englishStr.getBytes("ASCII");
//使用UNICODE编码
byte[] unicode = englishStr.getBytes("unicode");
System.out.println("使用ASCII编码后字节数组长度:"+ascii.length);
System.out.println("使用UNICODE编码后字节数组长度:"+unicode.length);

运行结果:

使用ASCII编码后字节数组长度:10
使用UNICODE编码后字节数组长度:22

果真UNICODE的字节数组长度确实是ASCII的两倍。

在这边稍做解释:为何UNICODE的字节数组的长度是22而不是20.

这是由于,UNICODE属于多字节编码,在存储多字节时,cpu有两种存储方式,分别是大端模式和小端模式。例如,“汉”字的UNICODE编码为6c49,在存储6c49时,CPU要判断是要将6c存在前面仍是把49存在前面,若是是大端模式,那么将先存6c,再存49,若是是小端模式,则先存49再存6c。unicode多出的两位其实就是指定使用哪一种存储模式,可是在编码过程当中是毫无心义的,也就是说Unicode确实要比ASCII花费两倍的空间来存储纯英文文本。

如今咱们来总结一下unicode的优缺点:

首先unicode在避免乱码上面功不可没,java底层的编码集就是使用的UNICODE,但同时,它在存储纯英文文本时要比ASCII花费两倍的存储空间,并且,它不与任何一种编码集兼容

UTF-8

在很长一段时间内,unicode一直没有获得普遍应用,直到互联网的出现,为了解决Unicode的传输问题,出现了一系列的新的编码:UTF系列

其中utf-8编码是在互联网上使用最广的一种UNICODE的实现方式。

utf-8最大的特色就是:它是一种可变长度的编码

image.png

utf-8对于不一样范围的unicode编码,都有不一样的长度来表示,分别使用1-4个字节表示一个符号

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

对于N字节的符号,第一个字节前N位都设为1,第N+1位设为0,后面的两个字节一概设为10,剩下没有说起的二进制所有为这个符号的Unicode码。

这样说是有点抽象,咱们来举一个栗子详细说明:

以汉字“严”为例,严的unicode码为4E25,转成二进制就是100111000100101,4E25处于00000800-0000FFFF中,因此严的utf-8的编码应为3个字节。接下来,将严的unicode二进制表示从最后一位开始依次从后向前填入格式中的X,多出的位补0,最终获得严的utf-8的表示:11100100 10111000 10100101,转为十六进制就是E4B8A5。

最后再来总结一下utf-8的优缺点:

优势:对于单字节编码,utf-8与ASCII是同样的,能够说utf-8就是ASCII的超集,因此大量只支持AASCII的遗留软件能够在UTF-8环境下继续工做;

   对于纯英文文本,相较于UNICODE,utf-8节约了一半的存储空间

缺点:好比刚才的栗子,“严”的unicode只须要两个字节,可是utf-8却须要3个字节,可见,在存储中文时,要花费1.5-2倍的空间

3、两种乱码状况

说了这么多,咱们只是为了要避免一个问题,那就是乱码。形成乱码的缘由有许多种,在这里只先向你们介绍两种:

第一种:中文成了看不懂的字符

String chineseStr = "哈哈";
//编码
byte[] gbks = chineseStr.getBytes("GBK");
//解码
String gbksStr = new String(gbks,"ISO-8859-1");
System.out.println("使用GBK进行编码,使用ISO-8859-1进行解码"+gbksStr );

运行结果:

使用GBK进行编码,使用ISO-8859-1进行解码¹þ¹þ

运行结果显示,中文变成了咱们看不懂的字符,那么,这种状况每每是因为,编码和解码使用了两种不兼容的编码集,致使中文变成了其余语言符号。

底层运行过程以下:

image.png

字符串转为字符数组,以后字符数组经过GBK转为字节数组,因为ISO和GBK的编码方式彻底不一样,因此将字节数组转为字符数组时“曲解”了语意。

第二种:中文变成了“?”

String chineseStr = "哈哈";
//编码
byte[] iso = chineseStr.getBytes("ISO-8859-1");
//解码
String isoStr = new String(iso,"ISO-8859-1");
System.out.println("使用ISO-8859-1进行编码,使用ISO-8859-1进行解码"+isoStr);

运行结果:

使用ISO-8859-1进行编码,使用ISO-8859-1进行解码??

运行结果显示,中文统一变成了“?”。那这又是为何呢?编码和解码的过程当中使用的相同的编码集怎么还会乱码呢?那是由于中文须要多字节编码,而ASCII是单字节编码,因为ASCII没法识别多字节,因此进行了过滤,将全部的中文都过滤成了“?”,底层执行原理以下:

image.png

相关文章
相关标签/搜索