zoukankan      html  css  js  c++  java
  • java编码详解

    举个例子

    我们在开发过程中,特别是多种编码格式并存的情况下,很容易遇到乱码问题。 假如有一个GBK编码java文件,然后再使用-Dfile.encoding=GBK参数,写入的文件中哪些是乱码呢。那如果使用UFT-8编码的java文件呢。

    public class Main {
        static String content = "中文";
    
        public static void main(String[] args) throws IOException {
            OutputStreamWriter gbkWriter =new OutputStreamWriter(new FileOutputStream("GBK-FILE"),"GBK");
    
           // 情况1、2、5的结果肯定是一致的
            gbkWriter.write(content+"
    "); //(1)
            gbkWriter.write(new String(content.getBytes("GBK"),"GBK")+"
    ");             // (2)
            gbkWriter.write(new String(content.getBytes("GBK"), "UTF-8")+"
    ");          // (3)
            gbkWriter.write(new String(content.getBytes("UTF-8"),"GBK")+"
    ");            // (4)
            gbkWriter.write(new String(content.getBytes("UTF-8"), "UTF-8")+"
    ");   // (5)
    
            gbkWriter.flush();
            gbkWriter.close();
    
        }
    }
    

      

    java编译到输出

    其实用一张图就可以清晰的概括出从java文件编译到输出的过程 输入图片说明 主要有3个地方的编码转换:

    编译过程

    上图①所示的位置,其实就是

    javac -encoding xxx

    的时候控制,如果你没有显示的指定编码,那么会根据当前操作系统的默认编码格式进行编译,一般windows是gbk,linux是UTF-8。如果这里编码指定错了,那么你的代码很有可能出现中文乱码问题,注意是很有可能,而不是绝对。原因后面会说到。编译出来的class文件统一都是UTF-8格式

    运行加载

    当class文件加载的jvm的时候,也会进行字符串编码转换,和前面一样,会使用操作系统默认的编码格式,这里的编码不是指class文件的编码,而是指java文件的编码格式,类似于指定java文件是什么编码格式编译为class文件的。就是jvm参数:

    java -Dfile.encoding=xxx

    在java运行过程中,字符串在内存中则是使用Unicode(UTF-16)进行存储的。而UTF-8转换为UTF-16是很简单的过程。当我们标用String.getBytes()时候,则是把内存中的unicode转换对应的字节数组。(如果没有指定,则使用操作系统默认的编码格式)。可以看出,把一个字符串从编译到内存,其实是经历的过程为:

    编译文件编码->加载jvm编码->unicode

    输出

    输出的编码则是在代码中指定的。例如: OutputStreamWriter gbkWriter =new OutputStreamWriter(new FileOutputStream("GBK-FILE"),"GBK");

    例子解析

    如果理解上面的,我们再看看文章一开始的例子。举几个例子做说明,其他的情况也就逐类旁通。

    例子1

    • java文件编码:GBK,
    • javac -encoding GBK
    • java -Dfile.encoding=GBK 那么使用GBK编码查看输出文件
    • (1)正常
    • (2)正常
    • (3)乱码
    • (4)乱码
    • (5)正常

    情况(1)

    情况1是比较好理解的,因为java文件编码、编译、加载都是使用GBK,加载到内存中Unicode肯定也是正常的,那么打印出来也是正常的。

    情况(2)和情况(5)

    在情况1的前提下(即加载到内存中是正常的),在jvm中使用GBK解码在编码肯定是正常的。

    情况(3)和情况(4)

    在情况1的前提下,使用不同的解码和编码,肯定是乱码

    特殊情况

    当我们使用UTF-8的格式打开文件的时候,情况(4)是正常的,其余都是乱码。其实是因为先使用unicode进行转换为UTF-8格式的Byte数组,生成的字符串虽然乱码和写文件的格式都是GBK,相当于原封不动的UTF-8格式的byte数组写到文件中,所以就会出现这个情况

    小节

    这个例子就是直至加载到内存都是正常的情况下,在jvm内进行编码和解码导致乱码的情况

    例子2

    • java文件编码:UTF-8,
    • javac -encoding GBK
    • java -Dfile.encoding=UTF-8

    那么使用GBK编码查看输出文件

    • (1)乱码
    • (2)乱码
    • (3)正常
    • (4)乱码
    • (5)乱码

    情况(1)

    情况1是乱码,说明字符串加载到内存中就已经是乱码了。因为UFT-8格式使用GBK进行编码,在生成class文件就已经是乱码了。

    情况(3)

    情况3为什么又是正常的呢,其实这是误打误撞类型。个人理解的造成这个情况的原因有:

    1. java文件的编码正好和jvm加载文件编码格式是一样的
    2. javac过程,相当于一个UTF-8->GBK格式转换,而content.getBytes("GBK"), "UTF-8")又相当于GBK->UTF-8的转换,两次转换正好相互抵消。

    使用UTF-8编码查看输出文件

    • (1)正常
    • (2)正常
    • (3)乱码
    • (4)乱码
    • (5)正常

    为什么是使用UTF-8打开情况1不是乱码了呢,其实和上面误打误撞,只不过之前发生在内存中的GBK->UTF-8换为我们在打开文件的时候进行的编码转换,情况1、2、5结果肯定一致的

    小结

    和例子1不一样,例子2是在生成class文件就已经是乱码的情况。这就是前面所说的,生成class乱码,但输出不一定是乱码。

    总结

    一句话:一定要保证java文件、编译、加载class文件、输出的编码格式一致。如果出现了乱码,可以从这几个阶段进行排查。

     
  • 相关阅读:
    20191224 Spring官方文档(Overview)
    20191224 Spring官方文档(启动)
    20191209 【归档】Linux就该这么学
    20191209 Linux就该这么学(6)
    20191209 Linux就该这么学(5)
    20191209 Linux就该这么学(4)
    20191209 Linux就该这么学(1-3)
    20191128 Spring Boot官方文档学习【目录】
    加密在卷积神经网络上的应用
    Federated Optimization: Distributed Machine Learning for On-Device Intelligence
  • 原文地址:https://www.cnblogs.com/lizo/p/7080611.html
Copyright © 2011-2022 走看看