快速学会区分“字符集”与“字符编码”——附:内码与外码区别
一、引言
日常复习总结,发现太久不看基础,很多概念的东西还是给忘了,本文将教你如何快速区分“字符”与“字符集”。
二、基础知识
2.1 字符
各种文字和符号的总称,包括各国家文字、标点符号、图形符号、数字等。 也就是说,它是一个信息单位,一个数字是一个字符,一个文字是一个字符,一个标点符号也是一个字符。
2.2 字节
字节是一个8bit的存储单元,取值范围是0x00~0xFF。 根据字符编码的不同,一个字符可以是单个字节的,也可以是多个字节的。
2.3 字符集
字符的集合就叫字符集。不同集合支持的字符范围自然也不一样,譬如ASCII只支持英文,GB18030支持中文等等。
在字符集中,有一个码表的存在,每一个字符在各自的字符集中对应着一个唯一的码。但是同一个字符在不同字符集中的码是不一样的,譬如字符“中”在Unicode和GB18030中就分别对应着不同的码(20013
与54992
)。
简单来说,字符集就是一个按规则排列的字符的集合,可以理解为现实生活中的各种“字典”。例如:牛津字典,剑桥商务字典等等。同一个“单词”在不同的字典中的位置是不同的,同理也不是所有的字典都支持各种语言的,例如ASCLL字符集里面就没有中文。
2.4 字符编码
定义字符集中的字符如何编码为特定的二进制数,以便在计算机中存储(就是将字符在字符集中的对应位置化为二进制)。字符集和字符编码一般一一对应(也有例外)
字符集与字符编码的一个例外就是Unicode字符集,它有多种编码实现(UTF-8,UTF-16,UTF-32等)
三、举例理解
其实通过上面的标蓝的句子相信大家已经有了区分的理解。下面再举出一些例子,来加深大家的理解:
-
ASCII码是一个字符集,同时它的实现也只有一种,因此它也可以指代这个字符集对应的字符编码。
-
GB18030是一个字符集,主要是中国人为了解决中文而发明制定的,由于它的实现也只有一种,所以它也可以指代这个字符集对应的字符编码。
-
Unicode是一个字符集(java默认),为了解决不同字符集码表不一致而推出的,统一了所有字符对应的码,因此在这个规范下,所有字符对应的码都是一致的(统一码),但是统一码只规定了字符与码表的一一对应关系,却没有规定该如何实现,因此这个字符集有多种实现方式(UTF-8,UTF-18,UTF-32),因此这些实现就是对应的字符编码。 也就是说,Unicode统一约定了字符与码表直接一一对应的关系,而UTF-8是Unicode字符集的一种字符编码实现方式,它规定了字符该如何编码成二进制,存储在计算机中。
附:常用编码表
ISO8859-1 | GBK/GBK2312 | UTF-8 | UTF-16 | UTF-32 | |
英文字符 | 1字节 | 2字节 | 大部分3,少部分4 | 2字节 | 4字节 |
中文字符 | 1字节 | 1字节 | 1字节 | 2字节 | 4字节 |
如表所示,ISO8859-1、utf-16、utf-32都是定长字符编码,其中utf-16(Unicode)更是jvm默认的内码编码。
内码 vs 外码
java在内部都是unicode编码,不存在什么GBK/UTF-8只有在输入输出的时候,才会发生与外部的编码转化问题。
简单来说,内码:char或String在内存里使用的编码方式。
外码:除了内码都可以认为是“外码”。(包括class文件的编码)
java内码:unicode(utf-16)
jvm默认外码:windows——gbk
Linux——utf-8
外码操作:
[String].getBytes() 就是将内码转换成外码存储在byte数组里,方法有两种形式:
-
getBytes(String charsetName): 使用指定的字符集将字符串编码为 byte 序列,并将结果存储到一个新的 byte 数组中。
-
getBytes(): 使用平台的默认字符集将字符串编码为 byte 序列,并将结果存储到一个新的 byte 数组中。
new String(byte[] bytes, String charset): 将字符序列bytes使用“charset”的字符编码(外码),解码成字符串(内码)。
编码和解码的“字符编码”必须要一致才能解码成想要的字符串(英文例外)。
但是为什么在tomcat 下,使用 new String(s.getBytes("iso-8859-1") ,"GBK") 却可以用呢? 答案是:
tomcat 默认使用iso-8859-1编码, 也就是说,如果原本字符串 str 是GBK的,tomcat传输过程中,将GBK转成iso-8859-1编码的字符数组了,相当于:
String s = "中国";
byte[] str_bytes = s.getBytes("GBK"); // 底层操作1:jvm,此时编码格式为gbk; String s = new String(str_bytes, "iso-8859-1"); // 底层操作2:tomcat; String str_new =new String(s.getBytes( "iso-8859-1"), "gbk"); // 得到正确字符串
默认情况下,使用iso-8859-1读取中文肯定是有问题的,那么我们需要将iso-8859-1 再转成GBK, 而iso-8859-1 是单字节编码的,即他认为一个字节是一个字符, 那么这种转换不会对原来的字节数组做任何改变,因为字节数组本来就是由单个字节组成的,如果之前用GBK编码,那么转成iso-8859-1后编码内容完全没变, 则 s.getBytes("iso-8859-1") 实际上还是原来GBK的编码内容则 new String(s.getBytes("iso-8859-1") ,"GBK") 就可以正确解码了。 所以说这是一种特殊情况。