Java编码浅析

Java字符串编码浅析
前些天在千手观音项目中,和算法端传参数的时候遇到了编码处理的问题,经过一段时间的学习摸索,对Java字符编码有了一定的了解,现将一篇总结分享给大家,希望大家指正~~

首先,对于Java来说,Java中的字符串都是Unicode编码的,Java的class文件采用的是utf-8的编码方式,JVM在运行时采用的是utf16的编码方式。
这里要说说Unicode编码了,Unicode实际上不是一种编码的实现,只是一种字符集,Unicode字符集又称为UCS(Unicode Character Set),而utf-8是对UCS-2(采用两个字节编码)的实现,utf-16是对UCS-4(采用四个字节编码)的实现。
所以可以这么说,JVM中所有的字符串资源都是Unicode的,也就是说,所有的String类型的数据都是Unicode编码的,网上有种说法就是,既然只有一种编码,那么可以简要的认为,JVM里面的String是不带编码的,String相当于char[]。

虽然所有的String对象都是Unicode的编码的,但是JVM中的byte[]数据是带编码的,比如GBK、UTF8、GB2312等等。
在JVM中,一个GBK编码的byte[]转化成String,实际上的操作是从GBK编码向Unicode编码的转换。一个String要转换成一个GBK编码的byte[],实际上的操作是从Unicode向GBK的转换。那么,一个GBK编码的byte[]要转换成Big5编码的byte[]呢?实际上可以先吧GBK的转成Unicode的,然后再转成Big5的,呵呵~~
所以,在Java中,Unicode是所有编码间转换的中间介质,所有的编码都可以转换到Unicode,而Unicode也可以转换到其他任意一种编码。根据Charset类的方法,我们可以得到,Java支持了160中字符集,这样任意字符集之间的转换,只需要160+160=320个方法就行了,如果是让这些字符集可以两两转换,则需要160*159=25440个方法!!!

那么,我们在什么情况下会遇到编码问题呢?遇到了编码问题该怎么解决呢?
1、 在外部资源(非数据库)读取数据的时候,因为要涉及到针对该外部文件的编码进行解码,所以我们需要使用该文件采用的字符集来读取文件内的数据:
例如:
InputStream is = new FileInputStream(“config/bp.properties”);
InputSteamReader streamReader = new InputStreamReader(is,”GBK”);
这里,我们采用GBK编码来读取config文件夹下的bp.properties中的数据,通过查看streamReader的encoding属性可以得到印证:
assertEquals(“GBK”, streamReader.getEncoding());
正是由于我们对bp.peoperties文件指定了正确的编码,所以在它转换成char数组时才能够正确的对其进行解码(从GBK到Unicode)。
char[] chars = new char[is.available()];
streamReader.reader(chars, 0, is.available());
然而,我们通常都是采用了一下的方式:
InputStream is = new FileInputStream(“config/bp.properties”);
InputSteamReader streamReader = new InputStreamReader(is);
这时候InputSteamReader是采用的JVM的默认字符集读取的外部文件,(如果没有修改过JVM参数,JVM默认字符集在windows下为GBK或GB2312,linux下为utf8) 。
一般来说,如果该文件的编码方式和操作系统使用的字符集一样,那么采用这种默认方式是可以的。但是如果文件编码方式和操作系统使用的字符集(一般和JVM默认字符集一致)不一样呢?那就是乱码了~~
当然,在这里,我们有必要说一下JVM的默认编码。这个默认的编码是在JVM启动时决定的,通常是根据语言环境和底层操作系统的charset来确定。可以通过下面的方法获取JVM的默认字符集:
Charset.defaultCharset();
然而由于这个默认值是“根据语言环境和底层操作系统的charset来确定”的,所以,其中任何一项变了,这个默认值都可能会变。比如说,在Xp下,咱们用eclipse自动new的Java文件都是GBK编码的,这时候Charset.defaultCharset() = “GBK”,但如果把这个Java文件的Text file encoding设为“UTF-8”,那么Charset.defaultCharset() = “UTF-8”,而且,javac命令也可以带上参数,明确指定编译时候的编码,例如:javac –encoding utf-8 Test.java
所以,在处理外部资源文件的时候,我们最好能够根据文件本身的编码对其进行由其编码到Unicode的转码。
其次,与此相似的,在向外部写数据的时候,也要将数据先转换成外部文件的编码才可以。
在千手观音项目中向算法接口post参数流的时候,BP从数据库中获取的中文是GBK编码的,而算法端是以UTF8读的,由于起初编码的时候没有指定流的编码,照成了编码的不一致,导致乱码。该问题通过下面的方式得到解决:
PrintWriter out = null;
URLConnection conn = realUrl.openConnection();
out = new PrintWriter(new OutputStreamWriter (conn.getOutputStream(), encoding));

2、 字符串和字节数组相互转换的时候,我们一般是采用"abc".getBytes()的方式将字符串转换成字节数组,但是这个转换采用的是什么编码呢?
和前面一样,"abc".getBytes(Charset.defaultCharset())具有同样的效果。也就是说,它根据JVM的默认编码(不一定是Unicode噢~)把字符串转换成字节数组。
那么,在由字节数组创建字符串的时候,我们一般默认是new String("abc".getBytes()),这样呢,同样是使用的系统的默认字符集来解码字节数组(例如从GBK到Unicode) 。
所以,我们最好明确指定编码,例如:
"abc".getBytes("GBK");//获取GBK编码的byte[]
new String("abc".getBytes("GBK"));
String类还有一个构造函数是:
public String(byte bytes[], String charsetName)
throws UnsupportedEncodingException
{
this(bytes, 0, bytes.length, charsetName);
}
API中对其解释是“通过使用指定的 charset 解码指定的 byte 数组,构造一个新的 String”。
但是如果对于new String(a.getBytes("GBK"),"UTF-8");来说呢?有人的解释是“将abc字符串从GBK编码转换成UTF-8编码”,那么这不和之前说的所有的字符串都是Unicode编码冲突了吗?
然而事实是:对于某个字符串,我们本应该用UTF-8来读取并解码字符串,但是结果却采用了GBK的方式,导致生成了一个错误的字符串a,要恢复,只能先把字符串恢复成原始字节数组,然后通过正确的UTF-8的编码将其再次编码成字符串。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值