Java中的乱码问题(一)

2014-11-24 02:29:18 · 作者: · 浏览: 0

Java中乱码问题很常见,原因也多种多样,这里做一个总结,不求全面,力求准确,如果错误欢迎指正。

1.文件页面编码导致的乱码。
每一个文件(java,js,jsp,html等)都有其本身的编码格式,文件中的代码在一种编码中显示正常,在另外一种编码下就会显示出乱码。
在Eclipse中,每一个工程都会有编码格式(Text file encoding), 一般默认为GBK。而一个比较好的编程习惯是新建一个项目,优先把项目的编码设为UTF-8。
这样做的原因很简单,UTF-8包含全世界所有国家需要用到的字符,是国际编码,通用性强。几种常见的字符集,GBK,GB2312,UTF-8之间的关系如下:
GBK是国家标准GB2312基础上扩容后兼容GB2312的标准。GBK、GB2312等与UTF8之间都必须通过Unicode编码才能相互转换

2.不同字符集的字符串转换时导致的乱码。
每一个String,底层实现都是用一个byte数组存储,使用不同的字符集,存储的数组长度当然就不同。如果不使用同一种字符集进行解码,就一定会出现乱码。
例如如下代码:
Java代码
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
public class TestCharset {

public static void main(String[] args) throws UnsupportedEncodingException {

String strChineseString = "中文";
String encoding = System.getProperty("file.encoding");
System.out.println("系统默认的字符集是:" + encoding);
System.out.println(strChineseString.getBytes(Charset.forName("GBK")).length);
System.out.println(strChineseString.getBytes(Charset.forName("UTF-8")).length);
System.out.println(strChineseString.getBytes().length);
}
}

输出结果为:
Java代码
系统默认的字符集是:UTF-8
4
6
6

可以看出,使用GBK和UTF-8编码,得到的byte数组长度不一样,原因就是utf-8使用3个字节来编码中文,而GBK使用2个字节来编码中文。因为我的项目默认使用UTF-8,所以使用不加参数的getBytes()得到的数组长度和使用UTF-8编码的 字符串长度一样。关于字符集的详细知识可以参考第一部分中给出的文章地址。
JDK中关于getBytes方法的描述:
getBytes() 使用平台的默认字符集将此 String 编码为 byte 序列,并将结果存储到一个新的 byte 数组中。
getBytes(Charset charset) 使用给定的 charset 将此 String 编码到 byte 序列,并将结果存储到新的 byte 数组。
每一个字符串底层都有自己的编码方式。不过一旦调用getByte方法后,得到的byte数组就是使用某种特定字符集编码后的数组,不需要再做多余的转换。
当得到上面的byte数组后,就可以调用String的另外一个方法来生成需要转码的String了。
测试例子如下:
Java代码

import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
public class TestCharset {

public static void main(String[] args) throws UnsupportedEncodingException {
String strChineseString = "中文";
byte[] byteGBK = null;
byte[] byteUTF8 = null;
byteGBK = strChineseString.getBytes(Charset.forName("GBK"));
byteUTF8 = strChineseString.getBytes(Charset.forName("utf-8"));
System.out.println(new String(byteGBK,"GBK"));
System.out.println(new String(byteGBK,"utf-8"));
System.out.println("**************************");
System.out.println(new String(byteUTF8,"utf-8"));
System.out.println(new String(byteUTF8,"GBK"));
}
}

输出结果为:
Java代码
中文

**************************
中文


可以看出,使用哪种字符集编码一个String,在生成一个String的时候就必须使用相应的编码,否则就会出现乱码。
简单来讲,只有满足如下公式的String转码,才不会乱码。
Java代码
String strSource = "你想要转码的字符串";
String strSomeEncoding = "utf-8"; //例如utf-8
String strTarget = new String (strSource.getBytes(Charset.forName(strSomeEncoding)), strSomeEncoding);

JDK中关于getBytes方法的描述:
String(byte[] bytes) 通过使用平台的默认字符集解码指定的 byte 数组,构造一个新的 String。
String(byte[] bytes, Charset charset) 通过使用指定的 charset 解码指定的 byte 数组,构造一个新的 String。

3.Socket网络传输时导致的中文乱码。
使用Socket进行通讯的时候,传输有多种选择,可以使用PrintStream,也可以使用PrintWriter。传输英文还好,传输中文就可能出现乱码问题。网上的说法很多,经过实际测试,发现问题还在字节和字符的问题上面。
众所周知,Java中分为字节流和字符流,字符(char)是16bit的,字节(BYTE)是8bit的。PrintStrean是写入一串8bit的数据的。 PrintWriter是写入一串16bit的数据的。
String缺省是用UNICODE编码,是16bit的。因此用PrintWriter写入的字符串,跨平台性好一些,PrintStream的可能会出现字符集乱码。
可以这样理解上面的