看了孙鑫老师的相关讲解后,总结一下。
String.getBytes(
"
gb2312
"
)就是把gb2312的文件转换为unicode码,
String.getBytes(
"
gb2312
"
,
"
iso-8859-1
"
)就是在转换为unicode后,再次编码为iso
-
8859
-
1
首先看清楚几种常用的字符集编码(java语言是采用unicode字符集编码来表示字符与字符串的):
ASCII(American Standard Code for Information Interchange,美国信息互换标准代码),是基于常用的英文字符的一套电脑编码系统。我们知道英文中经常使用的字符、数字符号被计算机处理时都是以二进制码的形式出现的。这种二进制码的集合就是所谓的ASCII码。每一个ASCII码与一个8位(bit)二进制数对应。其最高位是0,相应的十进制数是0-127。如,数字“0”的编码用十进制数表示就是48。另有128个扩展的ASCII码,最高位都是1,由一些制表符和其它符号组成。ASCII是现今最通用的单字节编码系统。
GB2312:GB2312码是中华人民共和国国家汉字信息交换用编码,全称《信息交换用汉字编码字符集-基本集》。主要用于给每一个中文字符指定相应的数字,也就是进行编码。一个中文字符用两个字节的数字来表示,为了和ASCII码有所区别,将中文字符每一个字节的最高位置都用1来表示。
GBK:为了对更多的字符进行编码,国家又发布了新的编码系统GBK(GBK的K是“扩展”的汉语拼音第一个字母)。在新的编码系统里,除了完全兼容GB2312 外,还对繁体中文、一些不常用的汉字和许多符号进行了编码。
ISO-8859-1:是西方国家所使用的字符编码集,是一种单字节的字符集 ,而英文实际上只用了其中数字小于128的部分。
Unicode:这是一种通用的字符集,对所有语言的文字进行了统一编码,对每一个字符都用2个字节来表示,对于英文字符采取前面加“0”字节的策略实现等长兼容。如 “a” 的ASCII码为0x61,UNICODE就为0x00,0x61。
UTF-8:Eight-bit UCS Transformation Format,(UCS,Universal Character Set,通用字符集,UCS 是所有其他字符集标准的一个超集)。一个7位的ASCII码值,对应的UTF码是一个字节。如果字符是0x0000,或在0x0080与0x007f之间,对应的UTF码是两个字节,如果字符在0x0800与0xffff之间,对应的UTF码是三个字节。
这是因为JAVA中默认的编码方式是UNICODE,而中国人通常使用的文件和DB都是基于GB2312或者BIG5等编码,故会出现此问题。以前我也经常为这个问题而苦恼,后来经查了些资料,终于解决了,我知道一定有很多朋友也会碰到这个问题,所以特就总结了一下,来拿出
来让大家一起分享了。
1、在网页中输出中文。
JAVA在网络传输中使用的编码是"ISO-8859-1",故在输出时需要进行转化,如:
String str="中文";
str=new String(str.getBytes("GB2312"),"8859_1");
但如果在编译程序时,使用的编码是“GB2312”,且在中文平台上运行此程序,不会出现此
问题,一定要注意。
2、从参数中读取中文
这正好与在网页中输出相反如:
str=new String(str.getBytes("8859_1"),"GB2312");
3、操作DB中的中文问题
一个较简单的方法是:在“控制面扳”中,把“区域”设置为“英语(美国)”。如果还会
出现乱码,还可进行如下设置:
取中文时:str=new String(str.getBytes("GB2312"));
向DB中输入中文:str=new String(str.getBytes("ISO-8859-1"));
4、在JSP中的中文解决:
在“控制面扳”中,把“区域”设置为“英语(美国)”.
在JSP页面中加入:
如果还不行正常显示,则还要进行下面的转换:
如:name=new String(name.getBytes("ISO-8859-1"),"GBK");
就不会出现中文问题了。
我们运行java程序时,JVM有自己所支持的编码种类,用以下代码可以看到:
Map m
=
Charset.availableCharsets(); //Charset类定义了用于创建解码器和编码器以及检索与 charset 关联的各种名称的方法。
Set names
=
m.keySet();
Iterator it
=
names.iterator();
while
(it.hasNext())
{
System.out.println(it.next());
}
然后可以通过以下代码看到我们目前JVM所使用的编码:
Properties pps
=
System.getProperties();
pps.list(System.out); // 将属性列表输出到指定的输出流。(此处指数到控制台)
具体来说什么是编码,什么是解码?
在InputStreamReader JDK有这样描述:It reads bytes and decodes them into characters using a specified charset.(用指定的字符集将字节数组解码成字符串)。
相反OutputStreamWriter 描述:Characters written to it are encoded into bytes using a specified charset.(用指定的字符集将字符串编码成字节数组)。
理解这个以后一切好办了啦!
我们的OS一般是GBK编码的(凡是从磁盘上读取文件可以看成是用OS的字符集编码方式来对操作对象进行解码处理--从标准输入设备读取数据的时候是依赖OS的字符集)。而我们将从磁盘上文件经过处理得到我们想要的字符串等其它对象的时候,这一过程是用JVM的默认的字符集编码方式来处理的!由于不同的字符集编码方式有着不同的原理(前面所述),这样当编码与解码不一致的时候,自然而然就出现了可爱的乱码。
比如如下,将我们JVM字符集改成iso-8859-1这样在就与我们的OS不同:
当输入中文时自然就输出的是乱码了。
pps.put(
"
file.encoding
"
,
"
ISO-8859-1
"
);
int
data;
byte
[] buf
=
new
byte
[
100
];
int
i
=
0
;
while
((data
=
System.in.read())
!=
'
q
'
)
{
buf[i]
=
(
byte
)data;
i
++
;
}
String str
=
new
String(buf,
0
,i);
System.out.println(str);
这时我们可以用string的一个构造方法:
String(byt[] bytes, String charsetName)
Constructs a new String by decoding the specified array of bytes using the specified charset.(用指定的字符集对字节数组进行解码)。
其中用到了string 的getBytes方法:
getBytes(String charsetName)
Encodes this String into a sequence of bytes using the named charset, storing the result into a new byte array.(用指定的字符集进行编码,将结果存放到一字节数组里面)重新构造一个string:
String strGBK
=
new
String(str.getBytes(
"
ISO-8859-1
"
),
"
GBK
"
);
这样又可以重新得到我们想要的汉字了。
我们这例子中是GBK(OS)来编码的,然后采用iso-8859-1(JVM)来解码得到一个新string(此string是乱码),然后将此string用iso-8859-1重新编码,并且用指定的GBK来解码。得到一个新string(也就是strGBK),这个string就不再是乱码了。
但如果我们一开始就采用GBK解码得到的字符串,然后用ISO-8859-1编码,能否再解码回去得到我们的中文字符呢?显示不可以啦,因为用ISO-8859-1的编码的时候采用是一种单字节的字符集来对其编码,这样就丢失了一个字节(对中文来说)!所以这样是得不到中文字符的!
2:读取Property文件时乱码处理
首先,我们的property文件大约如下:
#友情链接
news.link.inner.href = http:
//
www.baidu.com,
http://www.baidu.com
,
http://www.baidu.com
new
.link.inner.title = 百度1,百度2,百度3
乱码其实简单,只是开始的时候没注意罢了.我们的机器编码应该是GBK方式的,而在JVM程序中读取Property文件的时候使用的是Unicode编码方式,(我的这些处理过程也没对编码文件请求进行过滤),所以我们可以对其进行对应的编码.我是利用了JDK自带的native2ascii.exe工具.通过--encoding 来指定其编码方式
native2ascii -encoding GBK sourcefilename destfilename
这样你在
InputStream in = this.getClass().getResourceAsStream("/conf/netedu.properties"); //得到class/conf包中的资源文件
语句中用到的/conf/netedu.properties文件就是destfilename 来代替就OK了
只是这样你看到的可能是如下的一些代码:
#\u5385\u5185\u94fe\u63a5
-----
\u7528,\u9694\u5f00
news.link.inner.href http:
//
www.baidu.com,
http://www.baidu.com
,
http://www.baidu.com
new
.link.inner.title \u767e\u5ea61,\u767e\u5ea62,\u767e\u5ea63
当然你不可能对着一大堆的16进制看吧,所以可以通过 -reverse 来解码.
native2ascii -reverse sourcefilename destfilename