Java字符集编码与解码
字符(Character)是各种文字和符号的总称,包括各国家文字、标点符号、图形符号、数字等。字符集(Character set)是多个字符的集合,字符集种类较多,每个字符集包含的字符个数不同,常见字符集名称:ASCII字符集、GB2312字符集、BIG5字符集、 GB18030字符集、Unicode字符集等。计算机要准确的处理各种字符集文字,就需要进行字符编码,以便计算机能够识别和存储各种文字。
1、ASCII字符集
ASCII ((American Standard Code for Information Interchange): 美国信息交换标准代码)是基于拉丁字母的一套电脑编码系统,主要用于显示现代英语和其他西欧语言。它是最通用的信息交换标准,并等同于国际标准ISO/IEC 646。ASCII第一次以规范标准的类型发表是在1967年,最后一次更新则是在1986年,到目前为止共定义了128个字符。字符集ASCII不支持中文。
import java.nio.charset.Charset;
import java.util.Arrays;
public class TestCharacterSet {
public static void main(String[] args) {
//ASCII字符集编码不支持中文字符,如果遇到中文字符,则用?的编码63来代替
byte[] asciis = "我".getBytes(Charset.forName("ASCII"));
byte[] asciis2 = "是".getBytes(Charset.forName("ASCII"));
byte[] asciis3 = "谁".getBytes(Charset.forName("ASCII"));
System.out.println(Arrays.toString(asciis));//[63]
System.out.println(Arrays.toString(asciis2));//[63]
System.out.println(Arrays.toString(asciis3));//[63]
String ascii = new String(asciis, Charset.forName("ASCII"));
System.out.println(ascii);//?
//将数字强转为char类型,用的就是ASCII码
System.out.println((char)63);//?
}
}
2、GBK字符集
import java.nio.charset.Charset;
import java.util.Arrays;
public class TestCharacterSet {
public static void main(String[] args) {
//GBK字符集编码是支持中文的,一个中文字符用两个字节表示
byte[] gbks1 = "我".getBytes(Charset.forName("GBK"));
byte[] gbks2 = "是".getBytes(Charset.forName("GBK"));
byte[] gbks3 = "谁".getBytes(Charset.forName("GBK"));
System.out.println(Arrays.toString(gbks1));//[-50, -46]
System.out.println(Arrays.toString(gbks2));//[-54, -57]
System.out.println(Arrays.toString(gbks3));//[-53, -83]
//解码
String gbk1 = new String(gbks1, Charset.forName("GBK"));
String gbk2 = new String(gbks2, Charset.forName("GBK"));
String gbk3 = new String(gbks3, Charset.forName("GBK"));
System.out.println(gbk1);//我
System.out.println(gbk2);//是
System.out.println(gbk3);//谁
}
}
3、UTF-8字符集
import java.nio.charset.Charset;
import java.util.Arrays;
public class TestCharacterSet {
public static void main(String[] args) {
//UTF-8字符集编码是现在很常用的编码,一个中文字符常用三个字节表示
Charset charset = Charset.forName("UTF-8");
byte[] bytes1 = "我".getBytes(charset);
byte[] bytes2 = "是".getBytes(charset);
byte[] bytes3 = "谁".getBytes(charset);
System.out.println(Arrays.toString(bytes1));//[-26, -120, -111]
System.out.println(Arrays.toString(bytes2));//[-26, -104, -81]
System.out.println(Arrays.toString(bytes3));//[-24, -80, -127]
//解码
String s1 = new String(bytes1, charset);
String s2 = new String(bytes2, charset);
String s3 = new String(bytes3, charset);
System.out.println(s1);//我
System.out.println(s2);//是
System.out.println(s3);//谁
}
}
4、编码和解码的字符集不同时
乱码且字节丢失,无法还原
import java.nio.charset.Charset;
import java.util.Arrays;
public class TestCharacterSet {
public static void main(String[] args) {
Charset gbk = Charset.forName("GBK");
Charset utf_8 = Charset.forName("UTF-8");
//用GBK字符集编码
byte[] bytes = "我".getBytes(gbk);
System.out.println(Arrays.toString(bytes));//[-50, -46]
//用UTF-8字符集解码
String s = new String(bytes, utf_8);
System.out.println(s);//��
//用UTF-8编码
byte[] bytes1 = s.getBytes(utf_8);
System.out.println(Arrays.toString(bytes1));//[-17, -65, -67, -17, -65, -67]
//可见当utf-8无法正常解码的时候,就会用�的编码来代替
//再用GBK解码
String s1 = new String(bytes1,gbk);
System.out.println(s1);//锟斤拷
}
}
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class TestCharacterSet {
public static void main(String[] args) {
Charset gbk = Charset.forName("GBK");
Charset utf_8 = Charset.forName("UTF-8");
//用GBK字符集编码
byte[] bytes = "我是".getBytes(gbk);
System.out.println(Arrays.toString(bytes));//[-50, -46, -54, -57]
System.out.println(byteArrToBinary(bytes));
//[11001110, 11010010, 11001010, 11000111]
//用UTF-8字符集解码
String s = new String(bytes, utf_8);
System.out.println(s);//��
//用UTF-8编码
byte[] bytes1 = s.getBytes(utf_8);
System.out.println(Arrays.toString(bytes1));
//[-17, -65, -67, -17, -65, -67, -17, -65, -67, -17, -65, -67]
System.out.println(byteArrToBinary(bytes1));
//[11101111, 10111111, 10111101, 11101111, 10111111, 10111101,
// 11101111, 10111111, 10111101, 11101111, 10111111, 10111101]
//可见当utf-8无法正常解码的时候,就会用�的编码来代替
//再用GBK解码
String s1 = new String(bytes1,gbk);
System.out.println(s1);//锟斤拷锟斤拷
}
private static List byteArrToBinary(byte[] bytes){
List<String> list = new ArrayList<>();
int length = bytes.length;
for (int i = 0;i<length;i++){
String s = byteToBinary(bytes[i]);
list.add(s);
}
return list;
}
/**
* 字节转二进制
*/
private static String byteToBinary(byte b){
String s = Integer.toBinaryString(b & 0xFF);
return s;
}
}
乱码,无字节丢失,可以还原
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class TestCharacterSet {
public static void main(String[] args) {
Charset gbk = Charset.forName("GBK");
Charset utf_8 = Charset.forName("UTF-8");
//用UTF-8字符集编码
byte[] bytes = "我是谁".getBytes(utf_8);
System.out.println(Arrays.toString(bytes));//[-26, -120, -111, -26, -104, -81, -24, -80, -127]
System.out.println(byteArrToBinary(bytes));
//[11100110, 10001000, 10010001, 11100110, 10011000, 10101111, 11101000, 10110000, 10000001]
//用GBK字符集解码
String s = new String(bytes, gbk);
System.out.println(s);//鎴戞槸璋�
//用GBK编码
byte[] bytes1 = s.getBytes(gbk);
System.out.println(Arrays.toString(bytes1));
//[-26, -120, -111, -26, -104, -81, -24, -80, 63]
System.out.println(byteArrToBinary(bytes1));
//[11100110, 10001000, 10010001, 11100110, 10011000, 10101111, 11101000, 10110000, 111111]
//再用UTF-8解码
String s1 = new String(bytes1,utf_8);
System.out.println(s1);//我是�?
//可见在没有字节丢失的情况下,选择正确的编码之后可以还原,而最后一个字因为字节丢失,无法还原。
}
private static List byteArrToBinary(byte[] bytes){
List<String> list = new ArrayList<>();
int length = bytes.length;
for (int i = 0;i<length;i++){
String s = byteToBinary(bytes[i]);
list.add(s);
}
return list;
}
/**
* 字节转二进制
*/
private static String byteToBinary(byte b){
String s = Integer.toBinaryString(b & 0xFF);
return s;
}
}
5、UTF-8编码规则
UTF-8是一种变长字节编码方式。对于某一个字符的UTF-8编码,如果只有一个字节则其最高二进制位为0;如果是多字节,其第一个字节从最高位开始,连续的二进制位值为1的个数决定了其编码的位数,其余各字节均以10开头。UTF-8最多可用到6个字节。
如表:
1字节 0xxxxxxx
2字节 110xxxxx 10xxxxxx
3字节 1110xxxx 10xxxxxx 10xxxxxx
4字节 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
5字节 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
6字节 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
因此UTF-8中可以用来表示字符编码的实际位数最多有31位,即上表中x所表示的位。除去那些控制位(每字节开头的10等),这些x表示的位与UNICODE编码是一一对应的,位高低顺序也相同。
实际将UNICODE转换为UTF-8编码时应先去除高位0,然后根据所剩编码的位数决定所需最小的UTF-8编码位数。
因此那些基本ASCII字符集中的字符(UNICODE兼容ASCII)只需要一个字节的UTF-8编码(7个二进制位)便可以表示。
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class TestCharacterSet {
public static void main(String[] args) {
Charset gbk = Charset.forName("GBK");
Charset utf_8 = Charset.forName("UTF-8");
//用UTF-8字符集编码
byte[] bytes = "A".getBytes(utf_8);
System.out.println(Arrays.toString(bytes));//[65]
System.out.println(byteArrToBinary(bytes));
//[1000001]
//用GBK字符集解码
String s = new String(bytes, gbk);
System.out.println(s);//A
//用GBK编码
byte[] bytes1 = s.getBytes(gbk);
System.out.println(Arrays.toString(bytes1));
//[65]
System.out.println(byteArrToBinary(bytes1));
//[1000001]
//再用UTF-8解码
String s1 = new String(bytes1,utf_8);
System.out.println(s1);//A
}
private static List byteArrToBinary(byte[] bytes){
List<String> list = new ArrayList<>();
int length = bytes.length;
for (int i = 0;i<length;i++){
String s = byteToBinary(bytes[i]);
list.add(s);
}
return list;
}
/**
* 字节转二进制
*/
private static String byteToBinary(byte b){
String s = Integer.toBinaryString(b & 0xFF);
return s;
}
}
正是由于这样的编码规则,当遇到不符合utf-8编码规则的字节的时候,则会用特殊的符号代替。而英文字符在多个字符集编码中的编码与ASCII一致,所以没有出现英文乱码的情况。
6、字节流转字符流
创建一个txt文件,名为 测试.txt ,编码设置为UTF-8。
测试一下
字节流转字符流
编码问题
在这种文件名是中文的情况下,注意Java文件的编码与该文件的编码一致,也为UTF-8。
import java.io.*;
public class TestCharacterSet {
public static void main(String[] args) throws IOException {
//字节流
FileInputStream inputStream = new FileInputStream(new File("D:/测试.txt"));
//转换成字符流
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream,"UTF-8"));
//读取
String line = null;
while ((line=reader.readLine())!=null){
//打印
System.out.println(line);
}
}
}
在从字节流转换成字符流的时候,建议顺手设定好字符集编码规则。最好是各个文件的编码规则一致,不然会有乱码的风险。
控制台打印:
测试一下
字节流转字符流
编码问题