文章目录
- 前言
- ASCII
- GBK
- Unicode
- 为什么会出现乱码
前言
上一节关于字节流的文章中,在使用字节流读取本地文件中的数据时,文件中只存放了英文,而并没有存放中文数据。我们还提到了不建议使用字节流读取纯文本文件的数据,否则会出现乱码的情况,那么,为什么会出现这样的情况呢?相信探讨完今天的内容,你会有新的理解。
在计算机中,任何数据都是以二进制的形式存储的,一位二进制数称为一个比特位,一个字节由 8 位二进制数组成,存放 2 的 8 次方个数据,字节是计算机中最小的存储单元
。
而英文存放数据只需要一个字节即可,为什么呢?这就需要我们学习字符集的知识,字符集也叫作编码表,例如 ASCII 字符集,把一些常用的字符编写为一个表,每个字符对应了一个整数值,ASCII 表中一共 128 个数据,其中英文就全部包括在其中,所以说,存放英文数据至于要一个字节即可。
目前的文字编码标准主要有 ASCII、GB2312、GBK、Unicode 等。ASCII 编码是最简单的西文编码方案。GB2312、GBK、GB18030 是汉字字符编码方案的国家标准。Unicode 是全球字符编码的国际标准 。
ASCII
计算机以二进制的形式存储数据,例如 a,b,c 这样的字符和 1,2,3 这样的数字以及 + ,-,* 等这样的符号都是使用二进制在计算机中存储的,但是具体哪个二进制数字代表了哪个字符没有统一的标准,此时就需要规定一个标准,于是产生了 ASCII 字符集。ASCII 字符集由美国相关组织规定,其中共有 128 个数据。
详细的信息可以查看 ASCII 码表,常用的字符有 48 到 57 是十个阿拉伯数字,65 到 90 是 26 个大写英文字母,97 到 122 是小写英文字母。
在字符集中查询到的数据不会直接存储到计算机中,这里涉及到编码和解码的概念,编码就是把字符集中查询到的数据按照一定的规则进行计算,变成真实的存储在计算机硬盘中的二进制数据的过程,解码就是把实际存储在计算机中的二进制数据按照一定的规则进行计算,变成字符集中的数值的过程。ASCII字符集编码就是直接在前面补 0 ,解码时直接转换为十进制。
下面表示的是计算机的存储规则:
ASCII 表中只包含了一些欧洲国家常用的字符,并没有包含汉字。我们国家在1980年发布了 GB2312(国标)字符集,其中包含了常用图形字符和简体中文,台湾地区发布了繁体中文字符集 BIG5 ,为了统一标准,2000年我国发布了 GBK 字符集,包含了常用的中日韩文字等。随着计算机的发展,又出现了 Unicode 字符集,他将世界每个语言的字符集定义了一个唯一的编码。
我们使用的简体中文版 Windows 系统默认使用 GBK 字符集,不同的 Windows 使用不同版本的字符集,统一称为 ANSI 。
GBK
我们主要学习英文和汉字是如何使用字符集在计算机中存储数据的。
在 GBK 中,英问同样使用一个字节存储,因为它完全兼容 ASCII 字符集,在存储时编码规则与 ASCII 相同:
在 GBK 字符集中,汉字使用两个字节存储,因为一个字节最多存储 256 个数据显然是不够的。GBK字符集中,左边 8 位称为高位字节,右边 8 为称为低位字节,高位字节转为十进制必须是一个负数,且高位字节第一个二进制数一定是 1 ,这样做是为了区分汉字和英文,在读取数据时,当出现 1 时,计算机会认为读取到了汉字,此时会把两个字符的数据解码为一个汉字。
Unicode
不同的国家使用不同的字符集显然是不利于软件的发展的,为了统一规则,出现了 Unicode 字符集。Unicode 字符集由世界各地的软件开发商,电脑制造商等组成的统一码联盟制定的。
同样的,Unicode 完全兼容 ASCII 字符集,但是其编码规则有所不同。例如 UTF-16(使用 2-4 个字节),UTF-32(固定使用 4 个字节),UTF-8等,其中 UTF-8 是我们实际开发中常用的编码方式,其使用 1 到 4 个字节的可变字符编码。
在 UTF-8 字符编码规则中,ASCII字符集中的字符使用 1 个字节存储,中日韩文字等使用 3 个字节存储,其具以下的规则:
例如 “汉” 字,在 Unicode 字符集中是 47802,转化为二进制是1011101010111010
,其进行 UTF-8 编码以后是:
11101011 1010101010 10111010
为什么会出现乱码
在 Java 中程序出现乱码可能是下面的问题导致的:
- 读取数据时,未读完整个汉字
- 编码方式和解码方式不统一
我们知道使用字节流读取数据时,默认一次只能读取一个字节的数据,而 UTF-8 编码方式中使用三个字节存储一个汉字,此时就可能出现没有读完整个汉字的情况,于是就出现了乱码。
编码方式和解码方式不同时也会出现乱码。例如存储一个汉字时,使用 UTF-8 的方式编,计算机会使用 3 个字节存储该数据,如果此时使用 GBK 的方式解码,就会出现乱码的情况。
所以,为了避免出现乱码,我们不能使用字节流读取文本文件,并且要保证编码和解码的方式统一。
回到前言中的问题,现在我们已经不难理解。
Java 中使用以下的方法进行编码和解码:
import java.util.Arrays;
public class Test {
public static void main(String[] args) {
/*
编码和解码的使用
*/
String s="Java你好";
byte[] bytes = s.getBytes();
System.out.println(Arrays.toString(bytes));
String s2=new String(bytes);
System.out.println(s2);
}
}
此时,Java 使用 IDE 默认的编码方式进行编码,我们还可以使用 getBytes() 方法的重载方法指定编码方式,使用指定编码方式编码时可能会出现异常,抛出即可。如果编码和解码的方式不统一,会出现乱码。
示例:
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
public class Test {
public static void main(String[] args) throws UnsupportedEncodingException {
String s="Java你好";
byte[] bytes = s.getBytes("GBK");
System.out.println(Arrays.toString(bytes));
String s2=new String(bytes);
System.out.println(s2);
}
}
我们指定了字符串使用 GBK 的方式编码,当使用默认的 UTF-8 再次解码时,控制台出现乱码。