目录
一、引言
字符编码一直是萦绕在心头的痛,精华其实在如何编码上,但是由于平台、编码错乱、各种水文章的原因,导致一直在摸索中前进,以为认为的是对的,直到下次碰到问题才能更深一步理解,在这里碰到的问题总算差不多了,稍微总结一下以免后人栽倒坑里==
二、基础知识介绍
首先要了解的是字符集与字符编码的关系:
字符集就相当于汉语、英语、西班牙语等语种,其中确定了包含的文字和文字对应的编号;
字符编码指的是如何将这些文字对应的编号进行加工按照一定的法则排列;
字符集:
因为计算机美国人发明的,最早的字符集是ASCII字符集,然后欧洲人开始拓展了ASCII字符集为ISO8859-1,其中包含了西班牙语等一些字母;
后来计算机开始支持汉语,汉语也有了自己的字符集GB2312,由于汉字实在太多了,后来拓展为GBK字符集,两者相互兼容;
由于各个国家有各个国家(咯咯咯咯咯咯咯)的字符集,有些编号相同但是不同字符集对应的翻译的不同,瞎举例子0001在ascii字符集对应“s”,在GBK字符集对应的是“我”,开始搞了全球通用的一套编码unicode;
字符编码:
每一套字符集中都对应一种或者多种字符编码,
ASCII字符集——ASCII编码;ISO-8859-1字符集——ISO8859-1编码;
GB2312字符集——GB2312编码;GBK字符集——GBK编码;
Unicode字符集——UTF-16/UTF-8/UTF-32;
三、java中的编码问题
1、字符转内存
java中的基本类型byte、short、int、long;float、double;bool;char中,涉及到字符的是char和byte;
引用类型中主要是String,底层是char保存(jdk8以后的是byte保存);
String s = "君山";
char[] charsutf=s.toCharArray();
System.out.print("toCharArray:");
for (char temp:charsutf) {
System.out.print(Integer.toHexString(temp)+",");
}
System.out.println();
System.out.print("iso8859-1:");
byte[] iso = s.getBytes("iso8859-1");
//System.out.println(gbks.toString());
for (byte temp:iso) {
System.out.print(Integer.toHexString(temp)+",");
}
System.out.println();
System.out.print("GB2312:");
byte[] iso2312 = s.getBytes("GB2312");
//System.out.println(gbks.toString());
for (byte temp:iso2312) {
System.out.print(Integer.toHexString(temp)+",");
}
System.out.println();
System.out.print("gbk:");
byte[] gbks = s.getBytes("GBK");
//System.out.println(gbks.toString());
for (byte temp:gbks) {
System.out.print(Integer.toHexString(temp)+",");
}
System.out.println();
System.out.print("utf-8:");
byte[] byteutf= s.getBytes("utf-8");
for (byte temp:byteutf) {
System.out.print(Integer.toHexString(temp)+",");
}
System.out.println();
System.out.print("utf-16:");
byte[] byteutf16= s.getBytes("utf-16");
for (byte temp:byteutf16) {
System.out.print(Integer.toHexString(temp)+",");
}
结果:
toCharArray:541b,5c71,
iso8859-1:3f,3f,
GB2312:ffffffbe,fffffffd,ffffffc9,ffffffbd,
gbk:ffffffbe,fffffffd,ffffffc9,ffffffbd,
utf-8:ffffffe5,ffffff90,ffffff9b,ffffffe5,ffffffb1,ffffffb1,
utf-16:fffffffe,ffffffff,54,1b,5c,71,
我们来一个个解释:
首先,541b,5c71怎么来的?这个是java自己内部的机制,java会自动把String字符串内部解析为char[],每个char中的值如何确定的呢?是根据Unicode字符集确定的,unicode中“君”对应541b,“山”对应5c71;
那现在我们确定知道了java内部的存储字符集了,是unicode,当转换字符集(字符编码)的时候是怎么做的呢?先跳过两个,看gbk和utf-8,得到了一堆ffffff+十六进制的数字
“君山”两个字的GBK编码为BEFD、C9BD,对应我们的输出结果比较相似,把前面的ffffff去掉就是;
“君山”两个字的Unicode编码为541B、5C71,(这个正好对应了上一个unicode解释);
这里可以看到utf-8编码相似,去掉前面的ffffff就是结果;
其他的字符编码iso8859-1,GB2312、utf-16等都是类似的。
总结一下这里我们探讨的问题
- java内部字符存储字符集与字符编码;
- java中String.getByte(“charsetname”)获取的是不是和我们理解的字符集一致,结果是,使用这个最实质上是转换的字符集;
2、编码转换,内存转字符
首先说明下java中编码解码过程(以utf-8编码,gbk解码为例说明)
对应代码如下(注释//之间的部分为上述过程对应代码,其他代码为实验使用):
String s = "君山";
char[] charsutf=s.toCharArray();
System.out.print("toCharArray:");
for (char temp:charsutf) {
System.out.print(Integer.toHexString(temp)+",");
}
System.out.println();
System.out.print("iso8859-1:");
byte[] iso = s.getBytes("iso8859-1");
//System.out.println(gbks.toString());
for (byte temp:iso) {
System.out.print(Integer.toHexString(temp)+",");
}
String sss=new String(iso,"utf-8");
System.out.println("+"+sss);
System.out.println();
System.out.print("GB2312:");
byte[] iso2312 = s.getBytes("GB2312");
//System.out.println(gbks.toString());
for (byte temp:iso2312) {
System.out.print(Integer.toHexString(temp)+",");
}
sss=new String(iso2312,"GB2312");
System.out.println("+"+sss);
System.out.println();
System.out.print("gbk:");
byte[] gbks = s.getBytes("GBK");
//System.out.println(gbks.toString());
for (byte temp:gbks) {
System.out.print(Integer.toHexString(temp)+",");
}
sss=new String(gbks,"GBK");
System.out.println("+"+sss);
/////////////////////////////////////////////////////////////////
System.out.println();
System.out.print("utf-8:");
byte[] byteutf= s.getBytes("utf-8");
for (byte temp:byteutf) {
System.out.print(Integer.toHexString(temp)+",");
}
sss=new String(byteutf,"gbk");
System.out.println("+"+sss);
char[] chargbk=sss.toCharArray();
System.out.print("toCharArray:");
for (char temp:chargbk) {
System.out.print(Integer.toHexString(temp)+",");
}
System.out.println();
byte[] bbbb=toBytes("e590");
System.out.println(new String(bbbb,"gbk"));
////////////////////////////////////////////////////////////////////////
System.out.println();
System.out.print("utf-16:");
byte[] byteutf16= s.getBytes("utf-16");
for (byte temp:byteutf16) {
System.out.print(Integer.toHexString(temp)+",");
}
sss=new String(byteutf16,"utf-16");
System.out.println("+"+sss);
结果:
toCharArray:541b,5c71,
iso8859-1:3f,3f,+??
GB2312:ffffffbe,fffffffd,ffffffc9,ffffffbd,+君山
gbk:ffffffbe,fffffffd,ffffffc9,ffffffbd,+君山
utf-8:ffffffe5,ffffff90,ffffff9b,ffffffe5,ffffffb1,ffffffb1,+鍚涘北
toCharArray:935a,6d98,5317,
鍚
utf-16:fffffffe,ffffffff,54,1b,5c,71,+君山
在第一小节中我们知道了字符串在内存中的保存形式是char[],编码字符集为unicode,但是当java在编码转换的时候是怎么做的呢,这里以“君山”从utf-8转为gbk为例进行说明。
- 当我们新建String数组“君山”;
- 内存中char[]就以Unicode的形式保存了数组,为{541b,5c71};
- 获取utf-8编码的byte[],将Unicode进行utf-8编码(一般一个汉字对应三个byte),存储格式为{e5,90,9b,e5,b1,b1},逻辑上为{e5909b,e5b1b1};
- 将byte[]使用gbk解码(一般两个byte转为一个汉字)字符集转换但是byte数组存储并没有改变,存储格式为{e5,90,9b,e5,b1,b1},逻辑上为{e590,9be5,b1b1};
- 内存中byte[]转换为char[],以unicode字符集保存,为{935a,6d98,5317};
- 显示“鍚涘北”;
四、总结
- 字符集、字符编码;
- 内存中的字符unicode;
- 字符集转换unicode转utf-8,utf-8转gbk,gbk转Unicode;