java字符串编码转换过程中发生了啥?

背景:

有一个db链接,使用了latin1编码。有一个latin1的表,里面要存储UTF8或者GBK的中文。

然后在java代码层,为了把正确编码的中文落库,使用了网上流传着的这么一段编码转换的代码

public static String encodeLantin1(String s) {

    try {

        return new String(s.getBytes("GBK"), "ISO-8859-1");

    } catch (Exception e) {

        logger.error("转换错误!(可能为空值)");

    }

    return null;

}

这个代码的功能如下:

输入一个字符串s,将这个字符串编码为GBK,然后返回成latin1(ISO-8859-1)数据库可以存储的值。但是网上同时流传着一个说法:java底层使用UTF16存储字符串,所以不存在一个String是使用UTF8编码,或者GBK编码的。

 

作为一个c程序员,疑惑就出来了:既然java是使用UTF16存储的,那我转编码后,然后又用String存储,那我存到不就是一样的二进制吗?转编码有意义?或者更直白的,这个转编码过程,到底做了什么?

 

为了解决疑惑,我简单写了一个测试程序,内容如下

public class Main {



    private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();

    public static String bytesToHex(byte[] bytes) {

        char[] hexChars = new char[bytes.length * 2];

        for (int j = 0; j < bytes.length; j++) {

            int v = bytes[j] & 0xFF;

            hexChars[j * 2] = HEX_ARRAY[v >>> 4];

            hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];

        }

        return new String(hexChars);

    }

    public static void main(String[] args) {

        try

        {

            System.out.println("你好");

            System.out.println("direct:"+bytesToHex("你好".getBytes()));

            System.out.println("get as utf8:"+bytesToHex("你好".getBytes("UTF-8")));

            System.out.println("get as gbk:"+bytesToHex("你好".getBytes("GBK")));

            System.out.println("get as iso:"+bytesToHex("你好".getBytes("ISO-8859-1")));

            System.out.println("convert to utf8 and then get as iso:"+bytesToHex(new String("你好".getBytes(), "ISO-8859-1").getBytes("ISO-8859-1")));

            System.out.println("convert to gbk and then get as iso:"+bytesToHex(new String("你好".getBytes("GBK"), "ISO-8859-1").getBytes("ISO-8859-1")));

            System.out.println(new String("你好".getBytes(), "ISO-8859-1"));

        }catch(Exception e)

        {



        }

    }

}

输出结果如下

 

你好

direct:E4BDA0E5A5BD

get as utf8:E4BDA0E5A5BD

get as gbk:C4E3BAC3

get as iso:3F3F

convert to utf8 and then get as iso:E4BDA0E5A5BD

convert to gbk and then get as iso:C4E3BAC3

你好

 

所以这里传达了几个信息:

1,jvm的默认getbyte编码为utf8

2,使用不同编码进行getbyte,得到的结果不同

3,直接使用ISO-8859-1是不能拿到中文的正确编码的。只能拿到3F3F这个错误的编码(如果在latin1的数据库连接上,直接丢中文”你好“,数据库里得到的也是3F3F)

4,经过上述转换之后的string,使用ISO-8859-1取出来的bytes,跟从原始中文直接取对应的编码的bytes,得到的结果是一致的。

 

结合java使用UTF16存储文字,我们可以得到下面转换过程。

 

原始中文,存储java虚拟机的String -> 此时String使用UTF16编码,底层存储为原始中文的UTF16。

使用getBytes,按照UTF8编码取出bytes,中间经历了UTF16->UTF8。此时得到一个bytes数组,是该中文对应的UTF8编码的bytes

使用ISO-8859-1编码,把上面的bytes重新存进String中,此时String里面存的,是使用ISO-8859-1编码的字符,再通过UTF16转换后的字符。

再使用ISO-8859-1编码把bytes拿出来,此时经历了 UTF16 -> ISO-8859-1这个过程。

 

饶了这么大的圈,其实就是为了解决,某些中文的字符,没有对应的ISO-8859-1编码,直接丢给laitn1的db链接会转码失败这个问题。整个流程如下(左右两个框第一行为字符,第二行为对应的UTF16编码)

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值