【发布时间】:2020-07-05 09:18:52
【问题描述】:
平时,我不会在这里提问,但我所面对的问题是如此诡异,以至于我不能再独自一人解决它了,我已经筋疲力尽了。无论如何,我将描述我发现的所有东西,并且我发现了许多有趣的东西,我想相信它们会帮助别人帮助我。
软件版本: - 操作系统:Windows 10 专业版:1909 版本:18363.720 - IntelliJ IDEA:2019.2.4 Ultimate - Gradle 包装器版本:5.2.1-all - jdk: 8
编码存在问题,特别是在 Gradle 项目的控制台输出中。
这是我的 build.gradle 文件:
plugins {
id 'java'
id 'idea'
id 'application'
}
group 'com.diceeee.mentoring'
version 'release'
sourceCompatibility = 1.8
application.mainClassName('D')
compileJava.options.encoding = 'utf-8'
tasks.withType(JavaCompile) {
options.encoding = 'utf-8'
}
repositories {
mavenCentral()
jcenter()
}
dependencies {
testCompile group: 'junit', name: 'junit', version: '4.12'
}
我的源代码是带有 CRLF 的 UTF-8 编码,所以在 build.gradle 中我设置源代码应该使用 utf-8 编码而不是我的系统默认 windows-1251 编码进行编译。
这里是 D.java:
import java.io.FileWriter;
import java.io.IOException;
public class D {
public static void main(String[] args) throws IOException {
System.out.println(System.getProperty("file.encoding"));
String testLine = "Проверка работоспособности И Ш";
System.out.println(testLine);
FileWriter writer = new FileWriter("D:\\test.txt");
writer.write(testLine);
writer.close();
}
}
我还有 gradle.properties 一行:
org.gradle.jvmargs=-Dfile.encoding=utf-8
我检查了它是否有效,并向自己保证它有效,System.out 中的 Encoder 编码确实更改为 utf-8。
当我运行我的 gradle 项目时,我得到了这个:
21:04:53: Executing task 'D.main()'...
> Task :compileJava UP-TO-DATE
> Task :processResources NO-SOURCE
> Task :classes UP-TO-DATE
> Task :D.main()
UTF-8
�������� ����������������� � �
Deprecated Gradle features were used in this build, making it incompatible with Gradle 6.0.
Use '--warning-mode all' to show the individual deprecation warnings.
See https://docs.gradle.org/5.2.1/userguide/command_line_interface.html#sec:command_line_warnings
BUILD SUCCESSFUL in 0s
2 actionable tasks: 1 executed, 1 up-to-date
21:04:54: Task execution finished 'D.main()'.
还有更多信息。 1)我将输出留在代码中的文件中并非巧合。如果我们尝试查看文件,我们可以看到:
Проверка работоспособности И Ш
我不确定这是否正确,但我得出的结论是,问题出在控制台的某个地方,因为如果默认编码存在问题,则文件编写器对文件使用了错误的编码,并且输出将相等。但它不会发生。
2) 我已经调试了 PrintStream、OutputStreamWriter 和 StreamEncoder 类的内部结构。 StreamEncoder 确实使用 utf-8 字符集,它还将 utf-8 文本编码为正确的字节序列: String testLine = "Проверка работоспособности И Ш"; 每个西里尔字母为 2 个字节,空格为 1 个字节,如果我们计算所有字母,我们得到 57。
现在,看这里: Encoder debugging screen with resulting bytes
所以,正如我们所见,我们得到了前 57 个字节(其他来自其他输入,缓冲区使用限制):
[-48, -97, -47, -128, -48, -66, -48, -78, -48, -75, -47, -128, -48, -70, -48, -80, 32, -47, -128, -48, -80, -48, -79, -48, -66, -47, -126, -48, -66, -47, -127, -48, -65, -48, -66, -47, -127, -48, -66, -48, -79, -48, -67, -48, -66, -47, -127, -47, -126, -48, -72, 32, -48, -104, 32, -48, -88, 91]
看起来不错,西里尔字母编码为 [-48, -97], [-47, -128] 和其他 2 字节组,所以看起来不错,空格也匹配。所以,编码器做得很好,它可以工作,但是发生了什么? 我不知道。严重地。但还有更多信息。如果它看起来并不令人兴奋,我已经为你准备了其他东西。
我创建了一个干净的 Java 项目,没有任何 gradle/maven 等,只有我自己的 jdk,仅此而已。 程序相同:
package com.company;
import java.io.FileWriter;
import java.io.IOException;
public class Main {
public static void main(String[] args) throws IOException {
System.out.println(System.getProperty("file.encoding"));
String testLine = "Проверка работоспособности И Ш";
System.out.println(testLine);
FileWriter writer = new FileWriter("D:\\test.txt");
writer.write(testLine);
writer.close();
}
}
我运行它,我会得到什么?
"C:\Program Files\Java\jdk1.8.0_181\bin\java.exe" "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2019.2.4\lib\idea_rt.jar=58901:C:\Program Files\JetBrains\IntelliJ IDEA 2019.2.4\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_181\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\rt.jar;C:\Users\<my_removed_name>\IdeaProjects\test\out\production\test" com.company.Main
UTF-8
Проверка работоспособности И Ш
Process finished with exit code 0
在那之后,我就死了。发生了什么事???一会儿回到 gradle 项目。我做了一点修改:
import java.io.FileWriter;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
public class D {
public static void main(String[] args) throws IOException {
System.out.println(System.getProperty("file.encoding"));
String testLine = new String("Проверка работоспособности И Ш".getBytes(StandardCharsets.UTF_8), "windows-1251");
System.out.println(testLine);
FileWriter writer = new FileWriter("D:\\test.txt");
writer.write(testLine);
writer.close();
}
}
现在的输出是:
21:43:06: Executing task 'D.main()'...
> Task :compileJava
> Task :processResources NO-SOURCE
> Task :classes
> Task :D.main()
UTF-8
Проверка работоспособности �? Ш
Deprecated Gradle features were used in this build, making it incompatible with Gradle 6.0.
Use '--warning-mode all' to show the individual deprecation warnings.
See https://docs.gradle.org/5.2.1/userguide/command_line_interface.html#sec:command_line_warnings
BUILD SUCCESSFUL in 0s
2 actionable tasks: 2 executed
21:43:06: Task execution finished 'D.main()'.
在文件中:
Проверка работоспособности � Ш
此外,控制台中的这个输出是促使我确定出了什么问题的第一件事,我只是在编码,发现西里尔字母“И”确实有问题。我试图解决它,一次又一次......现在我在这里,因为我处于死胡同,我尝试了我在关于编码问题的类似问题和主题中找到的所有内容,我有红色一些关于java中默认编码的文章,Windows在控制台中使用cp866编码,默认为windows-1251编码,我们需要使用-Dfile.encoding=UTF-8显式确定编码,没有任何帮助,我什至不知道是什么寻找问题。原以为gradle不识别属性,charset还是windows-1251,但调试显示我错了。
嗯,这是我尝试解决问题的完整列表: 1)在idea.exe.vmoptions和idea64.exe.vmoptions中设置-Dfile.encoding=UTF-8并重启。没有帮助。 2) 在 IntelliJ IDEA -> Settings -> Editor -> File Encodings 各处设置 UTF-8。没有帮助。 3) 将 gradle 编译器编码设置为 utf-8。没有帮助。 4)设置gradle jvm选项org.gradle.jvmargs=-Dfile.encoding=utf-8。没有帮助。 5) 检查 Windows 是否将俄语语言作为不支持 unicode 以支持西里尔文的程序的默认设置。没有帮助。
我不确定 gradle 有什么问题,因为没有 gradle 的干净项目效果很好,控制台输出还可以。但是对于 gradle,西里尔符号是不正确的。此外,我尝试使用 getBytes(charset) 和 new String(byte[], charset) 方法/构造函数以某种方式更正控制台的输出,我尝试了这些变体:
String testLine = new String("Проверка работоспособности И Ш".getBytes(StandardCharsets.UTF_8), "windows-1251");
Output:
Проверка работоспособности �? Ш
不工作。
String testLine = new String("Проверка работоспособности И Ш".getBytes(StandardCharsets.UTF_8), "cp866");
Output:
?�?�???????�???? ?�???????�???�?????�?????????�?�?? ?� ?�
不工作。
String testLine = new String("Проверка работоспособности И Ш".getBytes(StandardCharsets.UTF_8), "utf-8");
Output:
�������� ����������������� � �
我们得到的结果没有任何转换。
另外,我还尝试了另一件事,就是 System.out 包装器来设置另一种控制台编码。
public class D {
public static void main(String[] args) throws IOException {
System.out.println(System.getProperty("file.encoding"));
System.setOut(new PrintStream(System.out, true, "utf-8"));
String testLine = "Проверка работоспособности И Ш";
System.out.println(testLine);
FileWriter writer = new FileWriter("D:\\test.txt");
writer.write(testLine);
writer.close();
}
}
我们仍然没有输出,甚至没有改变:
> Task :D.main()
UTF-8
�������� ����������������� � �
好吧,根据所有这些信息,我认为控制台本身确实不好,因为即使上面的代码的最后执行在文件中也有这个输出:
Проверка работоспособности И Ш
utf-8 编码,输出正确。但是 System.out.println 在控制台中打印出一些不合理的东西,即使 Encoder 工作得很好。我不知道这是怎么回事(脏话很抱歉),如果问题真的在 gradle 中,如何检查它?或者如何让 gradle 对控制台输出使用另一种编码?或者即使没有 gradle 的项目中的输出是正确的,它仍然是 IntelliJ IDEA 的东西?
我觉得自己像个侦探,但我已经停滞不前了,被困在那种情况下。如果有人帮助我,我将不胜感激。
【问题讨论】:
-
如果你用十六进制编辑器查看
D.java,字符串中的字符占用多少字节?看起来文件不是UTF-8,而是win-1251。 -
二手十六进制编辑器 Neo。它显示我的字符串如下:
Проверка работоспособности Р РЁ字节:d0 9f d1 80 d0 be d0 b2 d0 b5 d1 80 d0 ba d0 b0 20 d1 80 d0 b0 d0 b1 d0 be d1 82 d0 be d1 81 d0 bf d0 be d1 81 d0 be d0 b1 d0 bd d0 be d1 81 d1 82 d0 b8 20 d0 98 20 d0 a8看起来不错,d0 9f 真的是 'П' 字符。 -
啊,这似乎是 Gradle 特有的,是不是 stackoverflow.com/questions/21267234/…
-
我不确定问题到底出在哪里,但似乎问题确实是特定于 gradle 的。我尝试在链接中添加
systemProp.file.encoding=utf-8,也做到了:build { doLast { println System.getProperty("file.encoding") } }输出为:> Task :build utf-8。 -
我也有这个任务:
task info { doLast { println System.getProperty("file.encoding") println "Gradle — система автоматической сборки, построенная на принципах Apache Ant и Apache Maven, но предоставляющая DSL на языке Groovy вместо традиционной XML-образной формы представления конфигурации проекта. И Ш" } }当我运行时,我得到这个:> Task :info utf-8 Gradle � ������� �������������� ������, ����������� �� ��������� Apache Ant � Apache Maven, �� ��������������� DSL �� ����� Groovy ������ ������������ XML-�������� ����� ������������� ������������ �������. � �
标签: java gradle intellij-idea encoding utf-8