简单的编码介绍:
字符串 --> 字节数组:编码
字节数组 --> 字符串:解码
GBK编码: 汉字用两个字节表示,
第一种情况:
第一个字节是负数,第二字节负数
第二种情况:
第一个字节是负数,第二字节正数 eg:琲
UTF-8编码:汉字的编码都是 用 负数 表示
UTF-8会动态分配字节数,可能是 一个字节,两个字节,三个字节。
相关格式:低八位
上面这张图,可能大家会看不懂,那么我就在这里解释以下:
用UTF-8编码时:
对于汉字而言,至少用二个字节表示。
总之就是,
当用一个字节时,
字节一 的前一位是 0
当用二个字节时
字节一 的前三位是 110
字节二的前两位是 10
当用三个字节时
字节一 的前三位是 1110
字节二的前两位是 10
字节三的前两位是 10
相关代码如下:
import java.io.UnsupportedEncodingException;
public class Encode {
public static void main(String[] args) throws UnsupportedEncodingException {
// TODO Auto-generated method stub
/*
你好的UTF-8
11100100
10111101
10100000
11100101
10100101
10111101
你好的GBK
11000100
11100011
10111010
11000011
*/
String str = "你好";
byte[] buf = null;
buf = str.getBytes("utf-8");
printByte(buf);
}
private static void printByte(byte[] buf) {
// TODO Auto-generated method stub
for(byte b : buf) {
System.out.println( Integer.toBinaryString(b & 255) );
}
System.out.println();
}
}
我们可以看到
你好的UTF-8
11100100
10111101
10100000
11100101
10100101
10111101
满足上面的表达模式
你好的GBK
11000100
11100011
10111010
11000011
GBK不满足
**iso8859-1:**无法编码汉字
这是欧洲那边的编码。
乱码时:
如果你编错了,解不出来。
如果编对了,解错了,有可能有救。
现在我们验证 如果编对了,解错了,有可能有救。
import java.io.UnsupportedEncodingException;
public class BianMa {
public static void main(String[] args) throws UnsupportedEncodingException {
// TODO Auto-generated method stub
String str = "你好";
byte[] buf = null;
buf = str.getBytes("GBK");
printByte(buf);
String s = new String(buf,"iso8859-1");
System.out.println(s);
buf = s.getBytes("iso8859-1");
printByte(buf);
System.out.println(new String(buf,"GBK"));
}
private static void printByte(byte[] buf) {
// TODO Auto-generated method stub
for(byte b : buf) {
System.out.println( Integer.toBinaryString(b & 255) );
}
System.out.println();
}
}
输出结果是:
虽然出现了乱码,但是任然可以解析出来。
下面我们在来看看另一个例子:
import java.io.UnsupportedEncodingException;
public class BianMa {
public static void main(String[] args) throws UnsupportedEncodingException {
// TODO Auto-generated method stub
String str = "你好";
byte[] buf = null;
buf = str.getBytes("GBK");
printByte(buf);
String s = new String(buf,"UTF-8");
System.out.println(s);
buf = s.getBytes("UTF-8");
printByte(buf);
System.out.println(new String(buf,"GBK"));
}
private static void printByte(byte[] buf) {
// TODO Auto-generated method stub
for(byte b : buf) {
System.out.println( Integer.toBinaryString(b & 255) );
}
System.out.println();
}
}
输出结果:
我们可以发现,这次我们使用跟上面相似的思路去解决上面的问题,发现不起任何作用,下面我来说说这是为什么?
原因如下:
愿因一:
当“你好”用GBK编码好的二进制数,然后用UTF-8去编码时,发现这个不符合,自己的编码规则,于是,就用其他替换了,所以出现了乱码,
愿因二:
GBK用两个字节表示汉字,而UTF-8表示汉字至少两个字节。
当我们换成“谢谢”时是可以成功的。
原因:这时的GBK编码出来的二进制,符合UTF-8,所以是成功的。
当我们换成“联通”这两个字时:
发现GBK编码后的二进制是符合UTF-8的规则的,但还是没有解码成功,这是因为UTF-8进一步解码后,发现没有找到对应的字符,所以用其他代替,从而产生乱码。
相关测试:
在java中,字符串“abcd”与字符串“ab你好”的长度是一样,都是四个字符。
但对应的字节数不同,一个汉字占两个字节。
定义一个方法,按照最大的字节数来取子串。
如:对于“ab你好琲ab琲”,如果取三个字节,那么子串就是ab与“你”字的半个,
那么半个就要舍弃。如果去四个字节就是“ab你”,取五个字节还是“ab你”.
import java.io.IOException;
public class Test {
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
String str = "ab你好琲ab琲";
System.out.println("GBK");
for(int i=0;i<str.getBytes("GBK").length;i++)
System.out.println("截取"+(i+1)+"个字节: "+cutStringByGBK(str,(i+1)));
System.out.println("UTF-8");
for(int i=0;i<str.getBytes("UTF-8").length;i++)
System.out.println("截取"+(i+1)+"个字节: "+cutStringByUTF8(str,(i+1)));
}
private static String cutStringByUTF8(String str, int len) throws IOException {
byte[] buf = str.getBytes("UTF-8");
int count = 0;
for(int x = len -1 ;x>=0;x--) {
if(buf[x] < 0)
count++;
if(buf[x] > 0)
break;
}
if(count % 3 == 0)
return new String(buf,0,len,"UTF-8");
else if(count % 3 == 1)
return new String(buf,0,len-1,"UTF-8");
else
return new String(buf,0,len-2,"UTF-8");
}
private static String cutStringByGBK(String str,int len) throws IOException {
byte[] buf = str.getBytes("GBK");
int count = 0;
for(int x = len -1 ;x>=0;x--) {
if(buf[x] < 0)
count++;
if(buf[x] > 0)
break;
}
if(count % 2 == 0)
return new String(buf,0,len);
else
return new String(buf,0,len-1);
}
}