字符集是定义字符与二进制数字映射关系的抽象集合,使计算机能够处理各种文字和符号。常见的字符集包括ASCII、Unicode、GB2312和GBK。ASCII是最早的字符集,仅支持128个字符,适用于英文。Unicode则统一了全球字符的表示,支持多种编码方式如UTF-8、UTF-16和UTF-32。GB2312和GBK是中文编码标准,分别支持6763和21003个汉字。字符集转换在实际应用中常见,如将UTF-8编码的文本转换为GBK编码。在Java中,字符串内部使用UTF-16编码,与外部系统交互时需进行字符集转换,需注意字符集兼容性和数据丢失问题。
字符集
字符集是一个抽象的集合,它定义了字符与二进制数字之间的映射关系。计算机只能处理二进制数据,为了让计算机能够识别和处理各种文字、符号等字符,就需要将字符按照一定的规则编码成二进制数字,这个规则集合就是字符集。
常见字符集类型
- ASCII 字符集
- 基本介绍:美国信息交换标准代码(American Standard Code for Information Interchange),是最早的字符集。它用 7 位二进制数来表示一个字符,总共可以表示 128 个字符,包括英文字母、数字、标点符号等常用字符。
- 特点:由于当时计算机主要用于处理英文信息,ASCII 字符集基本能满足需求,且简单高效。但对于非英语国家的文字,如中文、日文、阿拉伯文等,ASCII 字符集就无法表示了。
- Unicode 字符集
- 基本介绍:为了统一表示世界上所有语言的字符,Unicode 字符集应运而生。它为每个字符分配了一个唯一的编号,无论何种语言、何种平台,只要使用 Unicode 编码,同一个字符的编号都是相同的。Unicode 涵盖了几乎所有人类语言的字符,包括汉字、日文假名、韩文、希腊字母、阿拉伯字母等。
- 编码方式:Unicode 有多种编码方式,如 UTF - 8、UTF - 16、UTF - 32 等。其中,UTF - 8 是最常用的一种,它可以用 1 到 4 个字节来表示一个字符,对于 ASCII 字符,仍然使用 1 个字节表示,保持了与 ASCII 字符集的兼容性;而对于其他非 ASCII 字符,则根据字符的不同使用不同长度的字节表示。UTF - 16 通常用 2 个字节表示一个字符,但对于一些生僻字符可能需要 4 个字节。UTF - 32 则固定用 4 个字节表示一个字符。
- GB2312 字符集
- 基本介绍:这是中国国家标准的简体中文字符集,全称为《信息交换用汉字编码字符集・基本集》。它收录了 6763 个常用汉字和 682 个非汉字字符,采用双字节编码方式,即每个字符用 2 个字节来表示。
- 应用场景:GB2312 在中国大陆地区的中文信息处理中得到了广泛应用,如早期的中文操作系统、办公软件等大多采用 GB2312 编码来处理中文文本。但随着汉字使用量的增加,GB2312 字符集逐渐不能满足需求。
- GBK 字符集
- 基本介绍:GBK 是对 GB2312 的扩展,它在 GB2312 的基础上增加了更多的汉字和符号,收录了 21003 个汉字和 883 个符号,共 21886 个字符。GBK 仍然采用双字节编码方式,向下兼容 GB2312,即所有 GB2312 编码的字符在 GBK 中仍然有效,且编码方式相同。
- 应用场景:GBK 在国内的中文信息处理领域也有广泛应用,特别是在处理一些包含生僻字的中文文本时,GBK 比 GB2312 更具优势。许多中文软件和数据库都支持 GBK 编码。
utf16参考之前教程。 2.1java数据类型,字符编码详解_java 查询字符串编码格式-CSDN博客
UTF-8使用1~4字节为每个字符编码:
·一个US-ASCIl字符只需1字节编码(Unicode范围由U+0000~U+007F)。
·带有变音符号的 拉丁文、希腊文、西里尔字母、亚美尼亚语、希伯来文、阿拉伯文、叙利亚文等字母则需要2字节编码(Unicode范围由U+0080~U+07FF)。
·其他语言的字符(包括中日韩文字、东南亚文字、中东文字等)包含了大部分常用字,使用3字节编码。
字符集的转换
在实际应用中,经常需要在不同字符集之间进行转换。例如,从网络上接收的文本可能是 UTF - 8 编码,而在本地系统中需要将其转换为 GBK 编码进行存储或显示。字符集转换的过程实际上是根据不同字符集的映射关系,将字符的二进制表示从一种编码方式转换为另一种编码方式。在 Java 中,可以使用 String
类的构造函数或 getBytes()
方法来进行字符集的转换。例如,将一个 UTF - 8 编码的字符串转换为 GBK 编码的字节数组可以这样做:
String str = "你好";
byte[] gbkBytes = str.getBytes("GBK");
在进行字符集转换时,需要注意字符集的兼容性和数据丢失问题。如果目标字符集不支持源字符集中的某些字符,可能会导致这些字符在转换过程中丢失或出现乱码。因此,在进行字符集转换时,要确保目标字符集能够完整地表示源字符集中的所有字符,或者对可能丢失的字符进行适当的处理。
基于 UTF - 16 的内部表示
在 Java 中,String
类用于表示字符串,其内部的字符序列是基于 UTF - 16 编码来存储的。UTF - 16 使用 16 位(2 字节)编码来表示大部分常见字符,它能够覆盖 Unicode 字符集的基本多文种平面(BMP),该平面包含了世界上大多数语言中常用的字符。对于 BMP 范围内的字符,每个字符用一个 16 位的代码单元表示;而对于超出 BMP 的字符(如一些生僻字符、表情符号等),则需要使用两个 16 位的代码单元(即代理对)来表示。
以下代码示例可以体现字符串在 Java 中的存储特点:
public class StringUTF16Example {
public static void main(String[] args) {
String str = "A你😀";
// 输出字符串的长度
System.out.println("字符串长度: " + str.length());
// 输出每个代码单元
for (int i = 0; i < str.length(); i++) {
char codeUnit = str.charAt(i);
System.out.printf("第 %d 个代码单元: %c, 十六进制值: %04X%n", i + 1, codeUnit, (int) codeUnit);
}
}
}
- 字符串
"A你😀"
包含一个 ASCII 字符'A'
、一个中文汉字'你'
和一个表情符号'😀'
。 length()
方法返回的是字符串中代码单元的数量,而非实际字符的数量。由于表情符号'😀'
需要两个代码单元(代理对)来表示,所以字符串长度为 4。charAt()
方法用于获取指定位置的代码单元。
遍历 非BMP 范围内的字符
str = "A你😀"; // 😀是非BMP字符
int length = str.codePointCount(0, str.length());
for (int i = 0; i < length; i++) {
int tmp = str.offsetByCodePoints(0, i);
int codePoint = str.codePointAt(tmp);
System.out.println(tmp+"\t" + codePoint);
System.out.println(Character.toChars(codePoint));
}
与其他字符集的交互
尽管 Java 字符串内部使用 UTF - 16 编码,但在和外部系统(如文件、网络)交互时,往往需要进行字符集的转换。例如,当把字符串写入文件或者通过网络传输时,通常会选择更节省空间的 UTF - 8 编码。Java 提供了相应的 API 来实现这些转换:
import java.io.UnsupportedEncodingException;
public class StringEncodingConversion {
public static void main(String[] args) {
String str = "你好";
try {
// 将字符串编码为 UTF-8 字节数组
byte[] utf8Bytes = str.getBytes("UTF-8");
System.out.println("UTF-8 编码后的字节数: " + utf8Bytes.length);
// 将 UTF-8 字节数组解码为字符串
String decodedStr = new String(utf8Bytes, "UTF-8");
System.out.println("解码后的字符串: " + decodedStr);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
getBytes("UTF - 8")
方法把字符串编码成 UTF - 8 字节数组。new String(utf8Bytes, "UTF - 8")
方法将 UTF - 8 字节数组解码成字符串。
Java 字符串的本质是基于 UTF - 16 编码存储的,这使得它能够方便地处理各种 Unicode 字符。不过在和外部系统交互时,需要根据具体需求进行字符集的转换
System.out.println("😀".equals("\uD83D\uDE00")); // true
System.out.println("😀".equals("\u0001F600")); //false
//java 在非BMP字符上,不是使用unicode码点保存字符的。而是utf16
字符集转换
1. 字符串编码为字节数组
可以使用 String
类的 getBytes()
方法将字符串编码为指定字符集的字节数组。
import java.io.UnsupportedEncodingException;
public class StringEncodingExample {
public static void main(String[] args) {
String str = "你好";
try {
// 使用 UTF-8 编码
byte[] utf8Bytes = str.getBytes("UTF-8");
// 使用 GBK 编码
byte[] gbkBytes = str.getBytes("GBK");
System.out.println("UTF-8 编码后的字节数: " + utf8Bytes.length);
System.out.println("GBK 编码后的字节数: " + gbkBytes.length);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
2. 字节数组解码为字符串
可以使用 String
类的构造函数将字节数组按照指定的字符集解码为字符串。
import java.io.UnsupportedEncodingException;
public class StringDecodingExample {
public static void main(String[] args) {
String str = "你好";
try {
// 使用 UTF-8 编码
byte[] utf8Bytes = str.getBytes("UTF-8");
// 将 UTF-8 编码的字节数组解码为字符串
String decodedStr = new String(utf8Bytes, "UTF-8");
System.out.println("解码后的字符串: " + decodedStr);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
注意事项
- 字符集不匹配导致乱码:在进行字符集转换时,若编码和解码使用的字符集不一致,就会出现乱码。例如,将一个使用 UTF - 8 编码的字节数组按照 GBK 字符集进行解码,就会得到乱码。
- 异常处理:在使用
getBytes()
方法和String
类的构造函数进行字符集转换时,可能会抛出UnsupportedEncodingException
异常,这意味着指定的字符集不被支持。因此,在代码中需要对该异常进行处理。