Java如何确定用于System.out
的编码?
鉴于以下课程:
import java.io.File; import java.io.PrintWriter; public class Foo { public static void main(String[] args) throws Exception { String s = "xxäñxx"; System.out.println(s); PrintWriter out = new PrintWriter(new File("test.txt"), "UTF-8"); out.println(s); out.close(); } }
它被保存为UTF-8,并在Windows系统上使用javac -encoding UTF-8 Foo.java
进行编译。
之后在git-bash控制台(使用UTF-8字符集)我做:
$ java Foo xxõ±xx $ java -Dfile.encoding=UTF-8 Foo xx├ñ├▒xx $ cat test.txt xxäñxx $ java Foo | cat xxäñxx $ java -Dfile.encoding=UTF-8 Foo | cat xxäñxx
这里发生了什么?
显然java会检查它是否连接到一个terminal,并在这种情况下改变它的编码。 有没有办法强制Java简单地输出纯UTF-8?
我也尝试了cmd控制台。 redirectSTDOUT似乎没有任何区别。 如果没有file.encoding参数,它会输出ansi编码,并用参数输出utf8编码。
我假设您的控制台仍在cmd.exe下运行。 我怀疑你的控制台真的期待UTF-8 – 我期望它是一个真正的OEM DOS编码(例如850或437) 。
Java将使用JVM初始化期间的默认编码集对字节进行编码 。
在我的电脑上复制:
java Foo
Java编码为Windows-1252; 控制台解码为IBM850。 结果: Mojibake
java -Dfile.encoding=UTF-8 Foo
Java编码为UTF-8; 控制台解码为IBM850。 结果: Mojibake
cat test.txt
猫解码文件为UTF-8; 猫编码为IBM850; 控制台解码为IBM850。
java Foo | cat
Java编码为Windows-1252; 猫解码为windows-1252; 猫编码为IBM850; 控制台解码为IBM850
java -Dfile.encoding=UTF-8 Foo | cat
Java编码为UTF-8; 猫解码为UTF-8; 猫编码为IBM850; 控制台解码为IBM850
这个猫的实现必须使用启发式来确定字符数据是否是UTF-8,然后将数据从UTF-8或ANSI(例如windows-1252)转换为控制台编码(例如IBM850)。
这可以通过以下命令来确认:
$ java HexDump utf8.txt 78 78 c3 a4 c3 b1 78 78 $ cat utf8.txt xxäñxx $ java HexDump ansi.txt 78 78 e4 f1 78 78 $ cat ansi.txt xxäñxx
cat命令可以做出这个决定,因为e4 f1
不是有效的UTF-8序列。
您可以通过以下方式更正Java输出:
HexDump是一个简单的Java应用程序:
import java.io.*; class HexDump { public static void main(String[] args) throws IOException { try (InputStream in = new FileInputStream(args[0])) { int r; while((r = in.read()) != -1) { System.out.format("%02x ", 0xFF & r); } System.out.println(); } } }