单字节编码。
最早的编码是iso8859-1
,和 ascii 编码相似。属于单字节编码,最多能表示的字符范围是 0-255,应用于英文系列。
iso8859-1是单字节编码,自身不能显示中文,若要显示中文,必须和其他能显示中文的编码配合,如:GBK
、UTF-8
。
以 “中文” 为例,这两个字不存在 iso8859-1 编码,以 gb2312 编码为例,应该是d6d0 cec4
两个字符,使用 iso8859-1 编码的时候则将它拆开为 4 个字节来表示:d6 d0 ce c4
(事实上,在进行存储的时候,也是以字节为单位处理的)。而如果是 UTF 编码,则是 6 个字节e4 b8 ad e6 96 87
。
由于是单字节编码,和计算机最基础的表示单位一致,所以在很多协议上,默认使用该编码。
双字节编码。
专门用来表示汉字,是双字节编码,兼容 iso8859-1 编码。其中 gbk 编码能够用来同时表示繁体字和简体字,而 gb2312 只能表示简体字,gbk 是兼容 gb2312 编码的。gb18030 又向下兼容 gbk。gb18030>gbk>gb2312
。
这是最统一的编码,可以用来表示所有语言的字符,而且是定长双字节(也有四字节的)编码,包括英文字母在内。不兼容iso8859-1编码的,也不兼容任何编码。很多软件内部是使用unicode编码来处理的,比如 Java。
不定长编码。
考虑到 unicode 不便于传输和存储。因此而产生了 utf 编码,utf 编码兼容 iso8859-1 编码,同时也可以用来表示所有语言的字符。
不过,utf 编码是不定长编码,每一个字符的长度从 1-6 个字节不等。另外,utf 编码自带简单的校验功能。一般来讲,英文字母都是用一个字节表示,而汉字使用三个字节。
将字符串按照 charset 编码,并以字节数组形式返回。比如 “中文”,如果 charset 为gbk
,则返回字节数组 [d6 d0 ce c4] 。如果 charset 为utf8
,则返回 [e4 b8 ad e6 96 87] 。如果是iso8859-1
,由于无法编码,最后返回 3f 3f ? ?。
String str = "中文"; //这是个unicode
byte[] gbk_bytes = str.getBytes("GBK"); //unicode -> GBK
byte[] utf8_bytes = str.getBytes("UTF-8"); //unicode -> UTF-8
byte[] iso8859_bytes = str.getBytes("ISO-8859-1"); //unicode -> ISO-8859-1
System.out.println(Charset.defaultCharset());
for(byte b : gbk_bytes){
System.out.println(Integer.toHexString(b));
}
System.out.println("------");
for(byte b : utf8_bytes){
System.out.println(Integer.toHexString(b));
}
System.out.println("------");
for(byte b : iso8859_bytes){
System.out.println(Integer.toHexString(b));
}
输出:
UTF-8
ffffffd6
ffffffd0
ffffffce
ffffffc4
------
ffffffe4
ffffffb8
ffffffad
ffffffe6
ffffff96
ffffff87
------
3f
3f
注:String str = “中文”; 这是个 unicode 编码
Charset.defaultCharset():获取当前环境字符编码。
指定字符编码的构造函数,将字节数组按照指定的字符编码进行解码,返回字符串。
String str = "中文"; //这是个unicode
byte[] gbk_bytes = str.getBytes("GBK"); //unicode -> GBK
byte[] utf8_bytes = str.getBytes("UTF-8"); //unicode -> UTF-8
byte[] iso8859_bytes = str.getBytes("ISO-8859-1"); //unicode -> ISO-8859-1
System.out.println(new String(gbk_bytes, "GBK"));
System.out.println(new String(utf8_bytes, "UTF-8"));
System.out.println(new String(iso8859_bytes, "ISO-8859-1"));
输出:
中文
中文
??
因为iso8859-1没有中文对应的编码,getBytes()
返回的就是未知字节数组,所以反向解析的时候,也是解析不出来的,所以返回乱码。
UTF-8 -> GBK、GBK -> UTF-8转换
public class ConvertDemo {
public static void main(String[] args) throws UnsupportedEncodingException {
String str="中文";
/* UTF-8 -> GBK */
byte[] utf8_bytes = str.getBytes("UTF-8");
System.out.println("转换前UTF-8=" + Arrays.toString(utf8_bytes));
byte[] gbkBytes = convert(utf8_bytes, "UTF-8", "GBK");
System.out.println("转换后GBK=" + Arrays.toString(gbkBytes));
System.out.println("转换后GBK=" + new String(gbkBytes, "GBK"));
/* GBK -> UTF-8 */
byte[] gbk_bytes = str.getBytes("GBK");
System.out.println("转换前GBK=" + Arrays.toString(gbk_bytes));
byte[] utfBytes = convert(gbk_bytes, "GBK", "UTF-8");
System.out.println("转换后UTF-8=" + Arrays.toString(utfBytes));
System.out.println("转换后UTF-8=" + new String(utfBytes, "UTF-8"));
}
public static byte[] convert(byte[] bytes, String fromCharsetName, String toCharsetName) throws UnsupportedEncodingException {
String string = new String(bytes, fromCharsetName);
byte[] bytes1 = string.getBytes(toCharsetName);
return bytes1;
}
}
输出:
转换前UTF-8=[-28, -72, -83, -26, -106, -121]
转换后GBK=[-42, -48, -50, -60]
转换后GBK=中文
转换前GBK=[-42, -48, -50, -60]
转换后UTF-8=[-28, -72, -83, -26, -106, -121]
转换后UTF-8=中文
getBytes()
指定需要转换的字符集进行编码,返回编码后的字节数组。String str = "中文"; //这是个unicode
byte[] gbk_bytes = str.getBytes("GBK"); //unicode -> GBK
byte[] utf8_bytes = str.getBytes("UTF-8"); //unicode -> UTF-8
byte[] iso8859_bytes = str.getBytes("ISO-8859-1"); //unicode -> ISO-8859-1
//UTF->GBK->UTF
String str11 = new String(utf8_bytes,"GBK");
String str12 = new String(str11.getBytes("GBK"),"UTF-8");
System.out.println(str11);
System.out.println(str12);
System.out.println("+++++++");
//UTF->ISO->UTF
String str21 = new String(utf8_bytes,"ISO-8859-1");
String str22 = new String(str21.getBytes("ISO-8859-1"),"UTF-8");
System.out.println(str21);
System.out.println(str22);
System.out.println("+++++++");
//GBK->UTF->GBK
String str31 = new String(gbk_bytes,"UTF-8");
String str32 = new String(str31.getBytes("UTF-8"),"GBK");
System.out.println(str31);
System.out.println(str32);
System.out.println("+++++++");
//GBK->ISO->GBK
String str41 = new String(gbk_bytes,"ISO8859-1");
String str42 = new String(str41.getBytes("ISO8859-1"),"GBK");
System.out.println(str41);
System.out.println(str42);
输出:
涓枃
中文
+++++++
ä¸æ–‡
中文
+++++++
����
锟斤拷锟斤拷
+++++++
ÖÐÎÄ
中文
String str11 = new String(utf8_bytes,"GBK");
字节数组 utf8_bytes = [e4 b8 ad e6 96 87],按照 GBK 解码,因为 GBK 固定两位解析,所以,解析结果是 涓枃。
String str12 = new String(str11.getBytes("GBK"),"UTF-8");
将解析结果涓枃
进行 UTF-8 编码,得到字节数组为[e4 b8 ad e6 96 87],再进行 UTF-8 解码,就会得到结果中文
。能转换成功根本原因在于:UTF-8的中文编码位数大于GBK。
UTF->ISO->UTF
同理,UTF->ISO->UTF 也能成功。
GBK->UTF->GBK:
String str31 = new String(gbk_bytes,"UTF-8");
字节数组gbk_bytes = [d6 d0 ce c4],按照 UTF-8 解码,因为 UTF-8 是变长字节编码,解析会失败,解析结果是 ����。
String str32 = new String(str31.getBytes("UTF-8"),"GBK");
将解析结果 ����
进行 UTF-8 编码,解析结果未知,故 GBK 编码也是乱码。
结论:
该函数用来设置 http请求或者相应的编码。
对于 request,是指提交内容的编码,指定后可以通过getParameter()
则直接获得正确的字符串,如果不指定,则默认使用iso8859-1编码,需要进一步处理。
值得注意的是在执行setCharacterEncoding()
之前,不能执行任何getParameter()
。而且,该指定只对POST方法有效,对GET方法无效。分析原因,应该是在执行第一个getParameter()
的时候,java将会按照编码分析所有的提交内容,而后续的getParameter()
不再进行分析,所以setCharacterEncoding()
无效。而对于 GET 方法提交表单是,提交的内容在 URL 中,一开始就已经按照编码分析所有的提交内容,setCharacterEncoding()
自然就无效。
解决在JSP页面获取表单的值时会出现乱码
原因:iso-8859-1 是JAVA网络传输使用的标准字符集,而gb2312
是标准中文字符集,当你作出提交表单等需要网络传输的操作的时候,就需要把 iso-8859-1 转换为 gb2312 字符集显示,否则如果按浏览器的 gb2312 格式来解释 iso-8859-1 字符集的话,由于两者不兼容,所以会是乱码。
有两种解决转换方法:
getParameter()
之前通过 request.setCharacterEncoding 设置字符编码。new String(str.getBytes("iso8859-1"), "UTF-8");
编码后解码。使用第三方包 detector
public static String getFileCharsetName(File file) throws Exception {
CodepageDetectorProxy detector = CodepageDetectorProxy.getInstance();
/*ParsingDetector可用于检查HTML、XML等文件或字符流的编码,
* 构造方法中的参数用于指示是否显示探测过程的详细信息,为false不显示。
*/
detector.add(new ParsingDetector(false));
/*JChardetFacade封装了由Mozilla组织提供的JChardet,它可以完成大多数文件的编码测定。
* 所以,一般有了这个探测器就可满足大多数项目的要求,如果你还不放心,可以再多加几个探测器,
* 比如下面的ASCIIDetector、UnicodeDetector等。
*/
detector.add(JChardetFacade.getInstance());
detector.add(ASCIIDetector.getInstance());
detector.add(UnicodeDetector.getInstance());
Charset charset = null;
try {
charset = detector.detectCodepage(file.toURI().toURL());
} catch (Exception e) {
e.printStackTrace();
throw e;
}
String charsetName = "GBK";
if (charset != null) {
if (charset.name().equals("US-ASCII")) {
charsetName = "ISO_8859_1";
} else if (charset.name().startsWith("UTF")) {
charsetName = charset.name();// 例如:UTF-8,UTF-16BE.
}else if(charset.name().equals("GB2312")){
charsetName="GBK";
}
}
return charsetName;//返回最终的编码格式
}