字符集与编码

发布时间 2023-06-18 22:26:49作者: devin1024

术语

字符(character)是具有语义值的文本的最小单位。
字符集(character set)是可能由多种语言使用的字符的集合。例:拉丁语字符集由英语和大多数欧洲语言使用,但希腊语字符集仅由希腊语使用。
编码字符集(coded character set)是一个字符集,其中每个字符对应于一个唯一的数字。
一个编码字符集的编码点(code point)是字符集中或编码空间中允许的任何值。
编码空间(code space)是一个整数范围,其值为编码点。
编码单元(code unit)是一个位序列,用于在给定的编码表单中对库的每个字符进行编码。有的文档中称之为编码值(code value)。

编码单元例子:US-ASCII: 7 bits,UTF-8, GB18030: 8 bits,UTF-16: 16 bits,UTF-32: 32 bits

字符串"abc?" 包括4个字符,4个编码点:
(1) UTF-32编码时: 4个编码单元,00000061, 00000062, 00000063, 00010400
(2) UTF-16编码时: 5个编码单元,0061, 0062, 0063, d801, dc00
(3) UTF-8编码时: 7个编码单元,61, 62, 63, f0, 90, 90, 80

编码格式区别

Unicode、GB2312都是编码字符集,即一个编号(数字)到字符的一种映射关系,仅仅是一种映射关系。

GBK、UTF-8都是编码格式。GBK和UTF-8都是用来序列化或存储Unicode编码的数据的,但是分别是2种不同的格式。除了格式不一样之外,他们所关心的unicode编码范围也不一样,UTF-8考虑了很多种不同国家的字符,涵盖整个Unicode码表,所以其存储一个字符的编码的时候,使用的字节长度也从1字节到4字节不等;而GBK只考虑中文(在Unicode中的一小部分字符的编码),所以它算好了只要2个字节就能涵盖到绝大多数常用中文(2个字节能表示6w多种字符),所以它存储一个字符的时候,所用的字节长度是固定的。

Java中的编码

首先Java的String使用的编码是Unicode,但是当String存在于内存中时(也就是当程序运行时,你在代码中用String类型的引用对它进行操作时,也就是String没有被存在文件中且也没有在网络中传输(序列化)时),是“只有编码而没有编码格式的”,所以Java程序中的任何String对象,说它是GBK还是UTF-8都是错的。GBK和UTF-8是编码格式而不是编码,String在内存中不需要“编码格式”(记住编码格式是在存文件或序列化的时候使用的), 它只是一个Unicode的字符串而已。

所以Java里面String是不带编码格式的,而String.toByteArray(String charsetName)得到的byteArray是带编码格式的,格式就是你传入的charsetName,我们不妨把toByteArray的这个过程叫做编码;另外,new String(byte bytes[], String charsetName)是把一个byte数组(带编码格式)以charsetName指定的编码格式翻译为一个不带编码格式的String对象,我们不妨把这个过程叫解码

String(byte[] bytes)通过使用平台的默认字符集解码指定的 byte 数组,构造一个新的 String。
String(byte[] bytes, Charset charset)通过使用指定的 charset 解码指定的 byte 数组,构造一个新的 String。

byte[] getBytes()使用平台的默认字符集将此 String 编码为 byte 序列,并将结果存储到一个新的 byte数组中。
byte[] getBytes(Charset charset)使用给定的 charset 将此 String 编码到 byte 序列,并将结果存储到新的 byte 数组。

public class Test {
    public static void main(String[] args) throws UnsupportedEncodingException {
        String str1;
        String str2;
        byte[] bytes;

        str1 = "你好";
        bytes = str1.getBytes("UTF-8"); // 用UTF-8编码序列化到字节数组中
        str2 = new String(bytes, "UTF-8"); // 使用UTF-8方式解码
        System.out.println(str2 + "123");

        bytes = str1.getBytes("GBK");
        str2 = new String(bytes, "GBK");
        System.out.println(str2 + "123");

        bytes = str1.getBytes("UTF-8");
        str2 = new String(bytes, "GBK");
        System.out.println(str2 + "123"); // UTF-8编码,GBK解码

        str1 = "浣犲ソ"; // 这是"你好"的GBK编码的字符串
        str2 = new String(str1.getBytes("GBK"), "UTF-8");
        System.out.println(str2 + "123");
    }
}

// 输出:
// 你好123
// 你好123
// 浣犲ソ123
// 你好123

参考资料

java中GBK编码格式转成UTF8,用一段方法实现怎么做? - 知乎 (zhihu.com)