Java中涉及的编码知识

 

发发牢骚

在工作中遇到不少Java编码问题,每次解决乱码问题,都花了较长的时间,非常影响工作效率! 
在工作中会经常遇到各种各样的问题,每次一个问题总是捣腾了很久,然后就解决了,但其实没有弄明白问题的本质。当下次遇到类似的问题,还是继续折腾,非常浪费时间。 
想要高效解决编码问题,当然要究其根源。以后遇到类似问题,可以顺藤摸瓜,轻松解决!

典型问题

  • Java采用哪种字符集
  • Java中一个字符需要几个字节存储
  • String.getBytes()方法哪种编码方式?IOS-8895-1、还是UTF-8?
  • 乱码产生的原因

如果能够回答以上问题,那么这篇文章可以Pass了!

将Unicode字符串转化成UTF-8、ISO-8859-1字符串

Java采用Unicode字符集,一个字符用两个字节存储。

以下是程序片段:将Unicode字符串转化成UTF-8、ISO-8859-1字符串

    String cnName = "I am 小佳";
    //Unicode字符集        
    System.out.println(printHexString(cnName.toCharArray()));
    //ISO-8859-1编码       
    System.out.println(printHexString(cnName.getBytes("ISO-8859-1")));
    //UTF-8编码 
    System.out.println(printHexString(cnName.getBytes("UTF-8")));
    /**
     * 字节数组转换成16进制
     * @param b
     * @return
     */
    private  String printHexString( byte[] b) {
        String a = "";
        for (int i = 0; i < b.length; i++) {
            String hex = Integer.toHexString(b[i] & 0xFF);
            if (hex.length() == 1) {
                hex = '0' + hex;
            }

            a = a+hex;
        }
        return a;
    }

    /**
     * 字符数组转化成16进制
     * @param b
     * @return
     */
    private  String printHexString(char[] b) {
        String a = "";
        for (int i = 0; i < b.length; i++) {
            String hex = Integer.toHexString(b[i] & 0xFFFF);
            if (hex.length() == 1) {
                hex = '0' + hex;
            }
            a = a+hex;
        }
        return a;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

字符串”I am 小佳”,由Unicode转换成UTF-8和ISO-8859-1,得到不同的字节(16进制表示),主要区别如下:

  1. 对于英文字符,无论采用何种编码方式,得到的编码结果相同。所以,英文字符不存在乱码问题
  2. 对于中文字符,不同的编码方式得到不同的结果;Unicode用2个字节存储中文,UTF-8用3个字节存储中文
  3. ISO-8859-1不支持中文,任何中文,采用该编码,都会转换成3F(3F映射的字符为’?’); 所以当中文用ISO-8859-1编码时,会生成一些列带问号的乱码’????????????????’
编码方式I am 
Unicode4920616D205C0F4F73
ISO-8859-14920616D203F3F
UTF-84920616D20E5B08FE4BDB3

乱码的产生

采用不同的编码方式进行 编码 和 解码 , 是生成乱码的根源。下面分析两种常见的乱码

    String cnName = "I am 小佳";
    //采用ISO-8859-1编码、UTF-8解码
    String iso88591Str = new String (cnName.getBytes("ISO-8859-1"), "UTF-8");
    //采用UTF-8编码、ISO-8859-1解码
    String utf8Str = new String (cnName.getBytes("UTF-8"), "ISO-8859-1");             
    System.out.println(iso88591Str);//打印结果,乱码:I am ??
    System.out.println(utf8Str);//打印结果,乱码:I am å°ä½³
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
源字符串编码解码转换结果
I am 小佳UTF-8ISO-8859-1I am å°ä½³
I am 小佳ISO-8859-1UTF-8I am ??

产生乱码”I am å°ä½³”的原因是:ISO-8859-1只能对单个字节编码 
产生乱码”I am ??”的原因是:ISO-8859-1会把所有的中文字符转化成3F,3F映射的字符为’?’

getBytes()的编码方式

String.getBytes()采用操作系统默认的编码方式,请看getBytes()源码 
获取默认编码在这个方法: defaultCharset(),该方法从关键 
“file.encoding”获取默认编码方式 
题外话:defaultCharset方法值得研究,该方法是线程安全的,并且用了缓存! 
如果你使用getBytes(),发生了中文乱码问题, 则需要设置file.encoding为UTF-8, 即可解决乱码问题

    public byte[] getBytes() {
        return StringCoding.encode(value, 0, value.length);

    }
    static byte[] encode(char[] ca, int off, int len) {
        //获取默认得编码方式
        String csn = Charset.defaultCharset().name();
        try {
            // use charset name encode() variant which provides caching.
            return encode(csn, ca, off, len);
        } catch (UnsupportedEncodingException x) {
            warnUnsupportedCharset(csn);
        }
        try {
            return encode("ISO-8859-1", ca, off, len);
        } catch (UnsupportedEncodingException x) {
            // If this code is hit during VM initialization, MessageUtils is
            // the only way we will be able to get any kind of error message.
            MessageUtils.err("ISO-8859-1 charset not available: "
                             + x.toString());
            // If we can not find ISO-8859-1 (a required encoding) then things
            // are seriously wrong with the installation.
            System.exit(1);
            return null;
        }
    }
    public static Charset defaultCharset() {
        if (defaultCharset == null) {
            synchronized (Charset.class) {
                //从操作系统获取默认得编码方式
                String csn = AccessController.doPrivileged
                    new GetPropertyAction("file.encoding"));
                Charset cs = lookup(csn);
                if (cs != null)
                    defaultCharset = cs;
                else
                    defaultCharset = forName("UTF-8");
            }
        }
        return defaultCharset;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41

总结

我这里只是抛砖引玉,讲述了Unicode转换UTF-8、ISO-8859-1的过程,以及乱码问题产生的根源。 
如果对编码问题感兴趣的话,可以进一步探讨GBK、GB2313、UTF-8的编码转换是否会产生乱码 
另外,编码问题很考验思维深度,有不少面试官会专门出编码的题目。所以,编码问题值得研究!

文章标签:  Java UTF-8 ISO-8859-1 编码
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值