前言:日常开发过程中也经常遇到乱码的问题,此处做一个梳理总结,乱码问题产生的根源在于中文与英文不同的编码格式,因此除非有朝一日我们改用汉语编程,否则乱码问题不可避免。本文首先列举并分析常见的几种编码格式,然后,解释java中编码转化的原理,最后是自己遇到过的乱码问题
1. 编码
计算机中存储信息以8bits为一个单元,叫做一个字节,所能表示的字符为0-255个显然是不够的,解决矛盾就需要使用char,而从char转化为字节的过程就是编码,反之就是解码,所以可以说,编码是为了存储,解码是为了阅读使用
2. 常见编码
1)ASCII 码 这个是大家最为熟悉的,也是我们接触和学习的第一种编码方式,一个字节的低七位来表示,共128个字符。
2)ISO-8859-1~ ISO-8859-15 是在ASCII码基础上制定的一系列编码规范,其中ISO-8859-1最常用。仍是单字节编码,总共能表示256个字符。
3)Unicode:最统一的编码,可以表示所有语言的字符,定长双字节,但不兼容ISO-8859-1,英文也用两个字节表示,不便于传输和存储
4)UTF-8/UTF-16 ISO提出的通用的Universal Code。16表示用两个字节表示一个字符,字符串操作时很方便,但是存储空间扩大了一倍,同样的信息,产生更大的网络传输流量。UTF-8变长编码,且完全兼容ASCII。是目前比较通用的一种编码格式。
5)GB2312/GBK 都是中文编码,不同之处在于GBK是在前者基础上进行了扩展,能表示更多的汉字,并兼容前者。
6)其他编码格式
3. java编码分析
内存:
java用String表示字符串,String类提供了转换到字节的方法和将字节转换为String的构造函数。
public static void main(String[] args) {
String str = "中文";
try {
byte[] gbk = str.getBytes("GBK");
System.out.println(gbk.length);
System.out.println(new String(gbk));
System.out.println(new String(gbk,"GBK"));
System.out.println(new String(gbk,"UTF-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
上述代码,首先按照GBK编码格式进行编码,得到其实际存储的字节码,然后用String的构造函数,在拼接成字符串,可以指定编码格式,也可以不指定,在不指定的情况下则会使用环境默认的编码格式,本机IDE是GBK格式编码,因此第二行和第三行输出一致,均能正确解码,而强行使用UTF-8则会乱码。运行结果如下:
4
中文
中文
????
String. getBytes(charsetName) 编码过程如下图所示:
I/O
I/O操作以读取为例,涉及接口如下图:
Reader 类是 Java 的 I/O 中读字符的父类,InputStream 类是读字节的父类,InputStreamReader 类就是关联字节到字符的桥梁,负责在 I/O 过程中处理读取字节到字符的转换,而具体字节到字符的解码实现由 StreamDecoder 去实现。
4. 常见编码问题处理
内存:
在服务器上执行curl命令中包含中文,后端解析这个命令的代码中使用request.getParameter(“中文”)出现乱码,进行一次 String catName = new String(str.getBytes("ISO-8859-1"),"gbk");简单转换之后则可以正常获取到中文字符串。
其实,JAVA在网络传输中一般使用是"ISO-8859-1",输出时需要进行转化,如:String str=new String(str.getBytes("环境编码格式"),"ISO-8859-1"); 经过网络编码后的中文,要正确显示在页面上一般需要使用类似Stirng str=new String(str.getBytes("ISO-8859-1"),"环境编码格式");这样的方式来解码。
I/O
使用InputStream.read()方法在数据流中读取字节,然后保存在一个byte[]数组中,最后转换为String。在我们读取文件时,读取字节的编码取决于文件所使用的编码格式,因此在转换为String过程中如果两者使用的编码格式不同也会出现编码问题。例如:stream.txt编码格式为UTF-8,那么通过字节流读取文件时所获得的数据流编码格式就是UTF-8,而我们在转化成String过程中如果不指定编码格式,则默认使用系统编码格式(此处应该是GBK)来解码操作,由于两者编码格式不一致,那么在构造String过程肯定会产生乱码,如下:
private static void IOSample() throws IOException{
File file = new File("c:/stream.txt");
InputStream input = new FileInputStream(file);
StringBuffer buffer = new StringBuffer();
byte[] bytes = new byte[128];
for(int i ; (i = input.read(bytes))!=-1;){
// buffer.append(new String(bytes,0,i));
buffer.append(new String(bytes,0,i,"UTF-8"));
}
System.out.println(buffer);
}
当我们使用注释掉的buffer.append(new String(bytes,0,i));这种方式去转化的时候就会乱码。
杩欐槸瑕佷繚瀛樼殑涓枃瀛楃
改为指定具体编码方式之后就得到了正确的输出:
这是要读取的中文字符