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   a m  
Unicode 49 20 61 6D 20 5C0F 4F73
ISO-8859-1 49 20 61 6D 20 3F 3F
UTF-8 49 20 61 6D 20 E5B08F E4BDB3

乱码的产生

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

    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-8 ISO-8859-1 I am å°ä½³
I am 小佳 ISO-8859-1 UTF-8 I 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 编码

猜你喜欢

转载自blog.csdn.net/qq_38136705/article/details/80808228