Как 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 и скомпилирован с javac -encoding UTF-8 Foo.java
в системе Windows.
Затем на консоли 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 проверяет, подключен ли он к терминалу и в этом случае меняет свою кодировку. Есть ли способ заставить Java просто выводить простой UTF-8?
Я тоже пробовал с консолью cmd. Перенаправление STDOUT, похоже, не имеет никакого значения. Без параметра 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
cat декодирует файл как UTF-8; cat кодируется как IBM850; консоль декодируется как IBM850.
java Foo | cat
Java кодируется как windows-1252; cat декодируется как windows-1252; cat кодируется как IBM850; консоль декодирует как IBM850
java -Dfile.encoding=UTF-8 Foo | cat
Java кодируется как UTF-8; cat декодирует как UTF-8; cat кодируется как IBM850; консоль декодирует как IBM850
Эта реализация cat должна использовать эвристику, чтобы определить, являются ли символьные данные 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();
}
}
}
cat
просто копирует байты такими, какие они есть. Очевидно, что он действительно пытается «исправить» кодировку при записи в терминал. Я также не знал, что git-bash действительно использует терминал IBM850 cmd. Этот ответ был действительно полезным.
System.out
кодирует байты, используя кодировку по умолчанию . Иногда это даже кодировка, используемая консолью.java Foo
выводит что-то отличное отjava Foo|cat
? - Кодировка по умолчанию должна быть такой же.