盒子
盒子
文章目录
  1. 1. 字符在Java中(JVM)到底是咋样的?
  2. 2. 从Java(内存)到文件里
  3. 3. One more thing
  4. 4. 参考资料

编码?你需要知道的那些事:4-Java为什么有乱码

这是之前写的文章,因为博客地址换了,重新发布下。

这个系列目前一共四篇文章,之后可能会增加Python3.x关于编码方面的内容。

编码?你需要知道的那些事:1-了解字符集

编码?你需要知道的那些事:2-明白编码

编码?你需要知道的那些事:3-Python2.x为什么有乱码

编码?你需要知道的那些事:4-Java为什么有乱码

更新:

关于Python3 的编码

声明: Unicode是指字符集,不是编码

在正式进入前, 我们用Java来查看系统文件默认编码和调用System.out.Print方法时的编码(我的是中文版的Windows)。

1
2
3
4
5
6
7
8
9
//查看默认文件编码
String encoding=System.getProperty("file.encoding");
System.out.println(encoding);
//输出: GBK
//查看默认System.out编码
OutputStreamWriter osw = new OutputStreamWriter(System.out);
System.out.println(osw.getEncoding());
//输出: GBK

现在还我清白了吧,中文Windows的默认编码确实是GBK,不过Java中System.out默认的编码也是GBK,而Python是UTF-8 , 下面我们可以开始例子(栗子)了。(先记住,是GBK哦)

1. 字符在Java中(JVM)到底是咋样的?

我们从上文知道,Python2.x进行字符的操作时,最好将他们全部解码为Unicode后再进行操作。 无独有偶,Java(JVM)中进行字符的操作是也是先将其转换为Unicode,然后进行操作的。我们先来看张图(这是一张对号符号,很酷是不是,我们可以看到它在Unicode中的编号,你也可以在这里查找)。

我们先来测试下这样写:

1
2
3
char infinite = '∞';
System.out.format("%X", Integer.valueOf(infinite));
//输出(正是编号) : 221E

我们再这样写:

1
2
3
char infinite = 0x221E;
System.out.println(infinite);
//输出(正是我们想要的字符) : ∞

完全正常, Java在内存中操作字符时,会将其转为Unicode , 输出时再用GBK编码。

我们再来看张图(这是一张对号符号,我们可以看到它在Unicode中的编号,你也可以在这里查找)。我们这里用它的编号来输出这个字符,看看是不是也和上面一样顺利。

1
2
3
char duihao = 0x2713;
System.out.println(duihao);
//输出(这下不是啦) : ?

shit, 太不给面子了吧。上面好好的,怎么就你特殊。放轻松,还记得我们上面说Java调用System.out.println时采用的是GBK编码吗! 其实GBK字符集中没有这个符号,哈哈! 所以它不能正确输出。,现在我们把项目的编码改为UTF-8,再试试(针对Eclipse: 选中项目->右键->选中属性->选择UTF-8编码)。

我们再次查看默认的输出编码:

1
2
3
4
5
6
7
8
9
//查看默认文件编码
String encoding=System.getProperty("file.encoding");
System.out.println(encoding);
//输出: UTF8
//查看默认System.out编码
OutputStreamWriter osw = new OutputStreamWriter(System.out);
System.out.println(osw.getEncoding());
//输出: UTF8

好了(确认是UTF-8),现在我们继续刚刚失败的例子:

1
2
3
char duihao = 0x2713;
System.out.println(duihao);
//输出(哈哈,对了吧!) : ✓

现在我们终于明白了,原来和Python一样,字符在Java中(JVM)处理的时候,也变成了Unicode中的编号。JVM的这种约定使得一个字符存在的世界分为了两部分:JVM内部和OS的文件系统。在JVM内部,统一使用Unicode表示,当这个字符被从JVM内部移到外部(即保存为文件系统中的一个文件的内容时),就进行了编码转换(我们这里开始是Java默认的GBK,后来我们改为了UTF-8)。

2. 从Java(内存)到文件里

当你想要把字符保存到文件中去的时候,我们要对字符进行编码。如果默认的话就是使用GBK, 但是我们可以选择为UTF-8(方法如上,改项目属性) 。

好了,现在我们将我们酷酷的符号保存到文件中去。(我们只演示第一个符号。注意:第一个符号用GBK或者UTF-8编码都行, 第二个用UTF-8吧。)

我们要保存的字符:

1
char infinite = '∞';
  1. 使用默认为GBK的编码保存(正常显示,文件为GBK编码)。

    1
    2
    3
    PrintWriter out=new PrintWriter(new OutputStreamWriter(new FileOutputStream("I:/GBk.txt")));
    out.write(infinite);
    out.close();
  2. 修改默认编码为UTF-8 (正常显示,文件为UTF-8编码)

    1
    2
    3
    4
    char infinite = '∞';
    PrintWriter out=new PrintWriter(new OutputStreamWriter(new FileOutputStream("I:/UTF-8.txt")));
    out.write(infinite);
    out.close();
  3. 默认编码为GBK,但是我们使用UTF-8保存(正常显示,文件为UTF-8编码)

    1
    2
    3
    4
    char infinite = '∞';
    PrintWriter out=new PrintWriter(new OutputStreamWriter(new FileOutputStream("I:/UTF-8.txt"),"UTF-8"));
    out.write(infinite);
    out.close();

上面使用的类OutputStreamWriter,就是可以指定编码的地方。(这里对Unicode值进行了编码, 用Java的话说就是字节流向字符流转换。)

从文件中读取数据也是一样的, 采用对应的编码去读取, 别人家是UTF-8,你用GBK(默认的)或者ASCII上,那可不就是乱套了吗!

3. One more thing

现在我们知道了,Java也是为了统一操作字符,现将其转换为为Unicode。 所以,在JVM中,字符是以Unicode中的编号值存在的。 当我们想要保存字符时,首先要将其进行编码(按正确的方式喔, 不然有些不能正常显示,像我们上面的例子一样),然后我们就可以写入文件中去了。(还要注意的就是,默认的编码方式,它给我们带来方便,有时可带来了不便。)

4. 参考资料

Java中的字符集编码入门(五)Java代码中的字符编码转换Part 1
Java中的字符集编码入门(三)GB2312,GBK与中文网页
how-to-get-console-charset

支持一下
扫一扫,支持forsigner