欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Java基础学习之用System.out.println()输出中文字符串乱码问题

程序员文章站 2022-07-15 13:06:20
...

问题描述:

在学习“java基本数据类型”相关知识点过程中,使用不同文本编辑器编写java源代码文件在cmd控制台中编译和运行过程中多次出现乱码情况。 

  • 电脑操作系统:win10 企业版
  • 文本编辑器:Notepad(Windows自带记事本)、EditPlus、Notepad++、Sublime Text3
  • 执行环境:Windows控制台(cmd)

1、字符编码

关于字符编码的产生背景此处不再赘述。简要说明目前主流语言的字符编码的演变。

Java基础学习之用System.out.println()输出中文字符串乱码问题

         字符编码规定了人类自然语言与计算机语言之间的互相转换方式。我们用各种高级编程语言所编写的程序是不能直接存储到计算机磁盘中的,计算机磁盘中的数据都是二进制格式的,所以一个 .java文件从文本编辑器中保存到磁盘上要经过一次编码 ,编码的方式可能是上图中的任何一种或其他的任何字符编码方式。编码之后存储在磁盘中的文本是一个个的字节,称为字节流。当我们用不同的应用程序来打开这个.java文件时,应用程序先对这个从磁盘中输入到内存中的 字节流文件进行 解码, 读取其中所描述的内容,然后进行其他的操作。如上图所示,不同体系编码之间是不兼容的,同体系新版本和老版本之间也可能是不兼容的,因此,要确保正确得到文件中所描述的内容,就必须确定解码方式和编码方式是相同的(或者解码方式是兼容编码方式的),这样才不会得到奇怪的内容。

          java源文件要经过javac编译成class文件,java虚拟机执行这个class文件并输出结果。决定java源文件编码格式的是编辑器在保存文本之前对文本编码所用的格式,决定javac读取java源文件所用格式的也有一个可指定的参数,决定虚拟机输出格式的是所用输出方法内部的参数,决定控制台如何读取虚拟机传递过来的数据的设置是当前活动代码页(chcp 936或chcp 65001),只有这一系列编码解码过程前后所用的字符编码是相同的或者后面步骤的兼容前面步骤的,就可以正确的在控制台显示我们想要的字符串。下面我们用几个简单的示例来说明一下window 10 企业版环境下控制台输出中文字符串如何解决乱码问题。

2、示例

(1)编辑器默认字符编码、如何查看已有文件的编码以及如何改变源文件的编码?

这里重点针对 GBK(中文简体)、UTF-8(国际通用)两种格式做一下探究。

准备工作:编辑器和分别用这些编辑器创建对应的默认编码格式的java源文件。

编辑器 Notepad EditPlus Notepad++ Sublime Text3
文件名 Notepad.java EditPlus.java Notepad11.java Sublime.java

测试代码:

//此处类名设置为对应的编辑器的名字,便于区分
public class Notepad11       
{
	public static void main(String[] args)
	{
		int num1=10;
		float num2=num1;
		System.out.println(num2);

		//主要测试中文字符串的输出。下面的编辑器名字设置为对应编辑器名称便于区分

		String str="我是用Notepad++创建的中文字符串";
		System.out.println(str);
		System.out.println(“Hello world!”);
	}
}

        目前我所用过的4个编辑器我认为用Notepad++ ,来查看和修改java源文件字符编码格式是最直接和最清晰正确的。我用4个编辑器分别创建对应名称的测试文件,并以各自默认的字符编码保存。再用Notepad++分别打开这四个文件查看其编码方式,如下图:

Notepad默认字符编码

Java基础学习之用System.out.println()输出中文字符串乱码问题

EditPlus默认字符编码

Java基础学习之用System.out.println()输出中文字符串乱码问题

Notepad++默认字符编码

Java基础学习之用System.out.println()输出中文字符串乱码问题

Sublime Text 3 默认字符编码

Java基础学习之用System.out.println()输出中文字符串乱码问题

经过测试可以得出四种编辑器默认保存新建文本的字符编码格式(如果是打开已有文本,保存前可能手动改变了文本的字符编码,那么保存时以当前使用的字符编码存储。):

编辑器 Notepad EditPlus Notepad++ Sublime Text3
文件名 Notepad.java EditPlus.java Notepad11.java Sublime.java

默认保存文件字符编码

ANIS ANIS UTF-8 UTF-8

这些编辑器都可以在正确读取并打开java源文件之后将文件转码以你想要的字符编码重新保存。这里有个隐藏的坑。Notepad(Windows自带的记事本)在选择另存为UTF-8格式时,系统实际存储的格式为UTF-8 BOM ,关于这二者的区别请自行百度。

我将上文中示例文件Notepad.java(现在是ANSI格式)通过记事本另存为UTF-8格式,

Java基础学习之用System.out.println()输出中文字符串乱码问题

再用Notepad++编辑器打开这个重新编码之后的Notepad.java,显示其字符编码格式为UTF-8-BOM

Java基础学习之用System.out.println()输出中文字符串乱码问题

小结:到这里搞清楚了4大编辑器保存java源文件时默认的字符编码,以及如何查看一个java源文件的字符编码,最后如何将一个java源文件另存为我们想要的字符编码。

(2)、编译和运行过程中调整字符编码来解决中文乱码问题

上面已经讨论了如何查看java源文件的字符编码。那这里就在已经知道一个源文件时什么字符编码的基础上研究,javac的解码方式和控制台的Codepage参数对最终输出中文字符串的影响。

此处均以临时改变参数来进行本次试验。至于如何永久改变控制台CodePage和javac解码方式参数,最后再说。

下面我画了一幅草图,大致表示一下从Java源文件到最后输出到控制台中的过程中文件的编码和解码经过。

Java基础学习之用System.out.println()输出中文字符串乱码问题

看懂上图就大致明白了java的跨平台性是如何实现的。Java虚拟机运行在操作系统上,java应用程序运行在java虚拟机上,java虚拟机默认的输出方式跟所在平台的默认方式是对应的,ava虚拟机(jvm)是为各个平台量身打造的,是不跨平台的,运行在Windows上的Java虚拟机就不能直接运行在苹果系统上, 当一个 .class文件被成功生成之后,拿到运行在不同平台上的jvm中执行,在各平台的默认参数未改变的情况下,最后各平台上输出的结果一定是相同的。当各个不同的平台都配置好了各自量身定做的Java虚拟机之后,所有用java语言编写的java应用程序就能实现跨平台一致性。

目前所学内容能够使用的输出方法就是System.out.println() ,这个方法默认以操作系统的默认字符编码输出字节流。现在我所学的阶段还不知道如何修改这个方法的默认参数。暂且将这当做已知条件,不影响后面试验。

从图中看出,红圈内部的编解码过程我们暂时无法修改参数,但我们知道其默认使用的是什么字符编码就行。下面创建2个测试文件,文件内容与上文示例代码相同。

文件名 UTF8.java GBK.java
字符编码 UTF-8(chcp 65001) GBK(chcp 936)

Java基础学习之用System.out.println()输出中文字符串乱码问题

 Java基础学习之用System.out.println()输出中文字符串乱码问题     

 测试一:上面两幅图,源文件为GBK编码,javac使用系统默认GBK解码,得到class文件。第一图cmd活动页936,能够正确显示所有字符。第二图将cmd活动页临时修改为65001,直接运行前面编译的class文件,得到结果如图,数字和英文字母全部显示正确,中文全部乱码。后面验证时先将文件夹中已存在的GBK.class文件删除,再用cmd以chcp 65001来重新生成一个新的GBK.class,输出结果与第二图一致。由此得出,cmd的活动代码页参数对java原文件的编译没有影响,对jvm输出字节流的解码方式有影响。所以这里要得到正确的中文显示,应该保持cmd的活动代码页为默认的936。以下图中代码页均为936.

 

   Java基础学习之用System.out.println()输出中文字符串乱码问题

上图,源文件为UTF8格式,javac解码格式为GBK,编译成功,生成了一个UTF.class文件,但是运行之后输出结果是数字和英文字母和标点符号正确,中文全是乱码。这很好解释,GBK编码和Unicode编码中都包含ASCII编码,且ASCII编码在GBK和Unicode编码中分配的编码相同,而测试代码中所涉及到的数字、标点符号、英文字母全部在ASCII编码表示 的范围之内,所以编译过程中,javac以GBK格式正确识别了UTF8.java中的数字、标点符号、英文字母,中文部分则读取到错误的内容。最终运行class文件输出到控制台中,数字、标点、英文字母全部正确,中文字符串全部乱码。

测试二:设置javac解码格式为UTF-8,分别用GBK和UTF-8格式的源文件进行编译测试。

Java基础学习之用System.out.println()输出中文字符串乱码问题

上图是用UTF8格式解码GBK格式编码的源文件,编译进行到第8行遇到注释,// 后面的中文全部无法识别,有一个无法识别的错误就报错一次。

Java基础学习之用System.out.println()输出中文字符串乱码问题      

上图:源文件编码格式为UTF-8,javac解码方式为UTF-8,前后对应,读取正确,输出正确。

总结:在win 10 企业版平台上,解决java程序中文输出乱码问题:

  • 1、不要改变控制台的代码活动页参数,保持默认936即可。

  • 2、不论自己用哪个编辑器编写的或者从其他任何地方得到的java源代码,在调用javac编译之前先要确定这个源文件的字符编码格式。

  • 3、然后为javac设置匹配源码格式的解码方式。(javac默认以系统字符编码格式读取源文件。要用UTF-8读取的话,代代码写法为: javac -encoding utf-8 *.java  其中*为任意文件名)。或者将源文件统一转码成javac默认的字符编码。总之就是要保证源文件编码格式和javac解码格式一致或者javac解码方式兼容源文件的编码方式

  • 4、我从踩过的坑中得出教训就是:别随便改系统默认的各种参数,保持默认就好。需要作出调整的时候,使用临时指令来改变参数效果和永久设置是相同的,却不会有永久改变系统参数带来的一系列想不到的麻烦

最后,本人新入行小白一枚,第一次写博客,难免有理解不到位的地方,请各路大神不吝赐教!