UTF-8 не выводит символы на консоль
У меня есть следующий код
public class MainDefault {
public static void main (String[] args) {
System.out.println("²³");
System.out.println(Arrays.toString("²³".getBytes()));
}
}
Но не может напечатать специальные символы на консоли
Когда я делаю следующее, я получаю следующий результат
$ javac MainDefault.java $ java MainDefault
С другой стороны, когда я его компилирую и запускаю вот так
$ javac -encoding UTF8 MainDefault.java $ java MainDefault
И когда я запускаю его с использованием флага кодировки файла UTF8, я получаю следующее
$ java -Dfile.encoding=UTF8 MainDefault
Похоже, это не проблема с консолью (Git Bash в Windows 10), поскольку она обычно печатает символы.
Спасибо за вашу помощь
Ответы
Ваш код не печатает правильные символы в консоли, потому что ваша программа Java и консоль используют разные наборы символов, разные кодировки.
Если вы хотите получить одинаковые символы, вам сначала нужно определить, какие наборы символов используются.
Этот процесс будет зависеть от «консоли», в которую вы выводите свои результаты.
Если вы работаете с Windows и cmd, как предлагает @RickJames, вы можете использовать chcpкоманду для определения активной кодовой страницы.
Oracle предоставляет полную информацию о кодировках, поддерживаемых Java, и соответствие с другими псевдонимами - в данном случае кодовыми страницами - на этой странице.
Этот ответ stackoverflow также предоставляет некоторые рекомендации по сопоставлению кодовых страниц Windows и кодировок Java.
Как вы можете видеть в условии ссылки, кодовая страница UTF-8является 65001.
Если вы используете Git Bash (MinTTY), вы можете следовать инструкциям @kriegaex, чтобы проверить или настроить UTF-8кодировку эмулятора терминала.
Linux и UNIX или производные системы UNIX, такие как Mac OS, не используют идентификаторы кодовых страниц, а используют локали. Информация о языковом стандарте может различаться в зависимости от системы, но вы можете либо использовать localeкоманду, либо попытаться проверить LC_*системные переменные, чтобы найти необходимую информацию.
Это результат выполнения localeкоманды в моей системе:
LANG="es_ES.UTF-8"
LC_COLLATE="es_ES.UTF-8"
LC_CTYPE="es_ES.UTF-8"
LC_MESSAGES="es_ES.UTF-8"
LC_MONETARY="es_ES.UTF-8"
LC_NUMERIC="es_ES.UTF-8"
LC_TIME="es_ES.UTF-8"
LC_ALL=
Как только вы узнаете эту информацию, вам нужно запустить свою программу Java с параметром file.encodingвиртуальной машины, соответствующим правильной кодировке:
java -Dfile.encoding=UTF8 MainDefault
Некоторые классы, например PrintStreamили PrintWriter, позволяют указывать, Charsetв каком виде будет выводиться информация.
Эта -encoding javacопция позволяет вам только указать кодировку символов, используемую исходными файлами.
Если вы используете Windows с Git Bash, подумайте также о прочтении этого ответа @rmunge : он предоставляет информацию о возможной ошибке в инструменте, которая может быть причиной проблемы и не позволяет терминалу работать правильно из коробки без необходимости для ручной настройки кодирования.
Я также использую Git Bash в Windows 10, и он мне подходит.
Вот как это печатается,
Версия терминала mintty 3.0.2 (x86_64-pc-msys)и Мои свойства текста были,
Итак, я попытался воспроизвести ваши результаты, изменив наборы символов;
Установив для параметра «Набор символов» значение CP437 (OEM codepage)(обратите внимание, что это автоматически изменило и языковой стандарт на C), я мог бы получить результат, как и вы.
И затем, когда я снова изменил его на UTF-8 (Unicode), я смог получить результат, как ожидалось!
Следовательно, очевидно, что проблема в наборе символов вашей консоли.
Шестнадцатеричные коды подходят для UTF-8. Возможно, ваш набор символов для Git Bash не UTF-8. Для меня это выглядит так:
Вывод на консоль также выглядит нормально:
Обновление 2020-09-13: Вот доказательство того, что chcp.com <codepage>делает не работу в Git Bash (mintty). Это не имеет никакого эффекта. Вам действительно нужно выбрать правильную кодовую страницу в диалоговом окне настроек mintty.
Обновление 2020-09-15: Хорошо, после того, как я прочитал ответ @rmunge, я обновился до Git 2.28 и смог воспроизвести проблему OP, а также использовать chcpобходной путь (в моем случае это не сработало, как описано @rmunge). Поскольку в последних версиях Git (или MSYS2 соответственно) так много ошибок, и я не хочу использовать его chcp.comизнутри Git Bash каждый раз, когда открываю новую консоль, я просто перешел на версию 2.15.1, которую использовал 3 года. без проблем раньше. Возможно, есть более поздние версии без ошибки консоли, я не пробовал, а просто использовал свой старый установщик из папки загрузок на моем компьютере. Я рекомендую всем сделать то же самое и теперь работать над этой уродливой ошибкой. В консольной версии без ошибок все работает так, как я описал.
Краткая версия:
Неожиданное поведение воспроизводится при следующих настройках:
Windows 10 с английским, немецким или французским языком или любым другим языком, который приводит к кодовым страницам ANSI и OEM, которые кодируют ² и ³ по-разному.
Git для Windows 2.27.0 (установлен с настройкой по умолчанию, т.е. настроен на использование MinTTY и отключена экспериментальная поддержка псевдоконсолей)
Исходный код хранится в кодировке UTF-8
Чтобы добиться правильного поведения:
Либо переустановите Git для Windows 2.27.0 и включите экспериментальную поддержку псевдоконсолей на последней странице установщика, либо обновитесь до последней версии 2.28.
Скомпилируйте свой код с помощью javac -encoding UTF8
Вызов java без переопределения file.encoding
Средняя версия:
Git для Windows 2.27.0 использует версию MSYS2 , которая не устанавливает кодовую страницу для MinTTY путем вызова SetConsoleCP, когда поддержка псевдоконсолей отключена. Среда выполнения Java определяет кодовую страницу System.out, вызывая GetConsoleCP . Поскольку кодовая страница не устанавливается, когда Java выполняется в терминале MinTTY, вызов завершается неудачно, и Java использует кодировку, возвращаемую в Charset.defaultCharset()качестве запасного варианта. Но при установке Windows, как описано выше, Charset.defaultCharset()возвращает Cp-1252, в то время как кодировка по умолчанию для консолей - Cp-850 . Две кодовые страницы не полностью совместимы. Это приводит к странному выводу.
Полная версия:
В Windows есть два типа кодовых страниц: кодовые страницы ANSI и OEM. Первый тип предназначен для приложений пользовательского интерфейса, которые не поддерживают Unicode, а второй - для консольных приложений. Оба типа кодируют один символ в 1 байт, но они не полностью совместимы.
Поэтому в Windows Java приходится иметь дело с двумя кодировками вместо одного:
Charset.defaultCharset()возвращает кодовую страницу ANSI (обычно cp-1252). Эта кодировка указывается системным свойством file.encoding . Если не указан в качестве аргумента VM, исполняемый файл java определяет кодовую страницу ANSI и добавляет системное свойство во время инициализации.String.getBytes()использует кодировку, возвращаемуюCharset.defaultCharset().System.outиспользует кодовую страницу OEM для консолей (обычно cp-850). Исполняемый файл java получает эту кодовую страницу, вызывая функцию GetConsoleCP, и устанавливает ее как значение для внутренних свойств системы, sun.stdout.encoding и sun.stdout.encoding . Когда вызов GetConsoleCP завершается неудачно, используется кодировка, возвращаемаяCharset.defaultCharset(). Это происходит только в том случае, если консоль, на которой выполняется java.exe, ранее не установила кодовую страницу OEM, вызвав SetConsoleCP
Так что же теперь происходит в упомянутой выше настройке?
$ javac MainDefault.java $ java MainDefault
Собственный вызов GetConsoleCP не работает из-за ошибки в MSYS2 . Поэтому System.outвозвращается к кодировке, возвращаемой Charset.defaultCharset()cp-1252. Но OEM-кодовая страница консоли - cp-850. Поэтому System.out.println («²³») дает неожиданный результат.
Исходный код хранится в UTF-8. Для кодировки «²³» в UTF-8 требуется 4 байта. Но из-за отсутствия параметра -encoding javac предполагает кодировку по умолчанию, которая использует один байт на символ. Поэтому он интерпретирует 4 байта как 4 символа. String.getBytesиспользует 1-байтовую кодовую страницу ANSI cp-1252 и поэтому возвращает 4 байта.
$ javac -encoding UTF8 MainDefault.java $ java MainDefault
С параметром -encoding UTF8 javac интерпретирует кодированный источник UTF-8 как UTF-8. Таким образом, 4 байта «²³» правильно распознаются как два символа. System.outкодирует два символа в cp-1252, что приводит к 2 байтам. Но поскольку консоль по-прежнему использует cp-850, вывод все равно поврежден. String.getBytesкодирует два символа также в cp-1252, что приводит к 2 байтам.
$ java -Dfile.encoding=UTF8 MainDefault
Системное свойство file.encoding переопределяет кодировку, возвращаемую, Charset.defaultCharset()которая также используется String.getBytes(). Два символа, которые сначала неправильно интерпретировались javac как 4 символа в 8-битной кодировке, теперь корректно закодированы в UTF-8 как два символа, закодированные по два байта на символ. Это приводит к 4 байтам. Поскольку file.encoding не влияет на кодировку, которая используется System.out4 (а не 2 из-за неправильной интерпретации javac) символов, все еще закодированы в cp-1252, консоль по-прежнему использует cp-850, и вы все равно испорченный вывод.
Ваша консоль может печатать ²³, поскольку 8-битная OEM-кодовая страница консоли (cp-850) поддерживает оба символа. Но он кодирует его немного иначе, чем кодовая страница ANSI cp-1252, которая используется System.out;-)
В Windows это связано с вашей кодовой страницей. Вы можете использовать команду chcp, чтобы установить нужную кодовую страницу (например, если вы хотите настроить ее для конкретной запущенной программы), или вы можете указать кодировку, соответствующую кодовой странице, в командной строке java.
Если текущая кодовая страница не поддерживает символы, которые вы печатаете, вы увидите мусор в консоли.
Причина, по которой разные оболочки могут вести себя по-разному, связана с кодовой страницей / наборами символов, которые загружаются по умолчанию.
Пожалуйста, ознакомьтесь с этим сообщением SO, чтобы узнать, как это делается: Кодировка символов System.out
Hex C2B2 C2B3при интерпретации как UTF-8 - это ²³.
Я предполагаю, что вы используете Windows "cmd terminal"?
Команда «chcp» управляет «кодовой страницей». chcp 65001 предоставляет utf8, но для него также требуется установка специальной кодировки. Чтобы установить шрифт в окне консоли: щелкните правой кнопкой мыши заголовок окна → Свойства → Шрифт → выберите консоль Lucida.
Пожалуйста , убедитесь , что установка Windows 10 это не имеет поддержки Unicode UTF-8 включен. Вы можете увидеть эту опцию, перейдя в «Настройки», а затем: «Все настройки» -> «Время и язык» -> «Язык» -> «Настройки административного языка».
Вот как это выглядит - функция должна быть снята.
Обоснование:
"²³".getBytes()возвращает кодировку строки на основе обнаруженной кодировки по умолчанию. В системе Windows 10 кодировка по умолчанию обычно должна быть однобайтовой кодировкой, независимо от того, запускаете ли вы java.exe из консоли Windows или из Git Bash. Но ваш первый снимок экрана показывает 4-байтовую кодировку, которая на самом деле является UTF-8. Таким образом, ваша JVM, похоже, обнаруживает UTF-8 как неправильную кодировку по умолчанию, несовместимую с кодовой страницей вашей консоли.
Ваша консоль может печатать ²³, потому что оба символа поддерживаются используемой кодовой страницей, но кодировка основана на одном байте на символ, в то время как кодировка UTF-8 требует 2 байта для каждого из этих двух символов.
У меня нет простого объяснения вашего второго снимка экрана, но имейте в виду, что Git Bash основан на MSYS2, который снова использует эмулятор терминала mintty . Хотя MSYS2 использует UTF-8, а mintty, похоже, также поддерживает UTF-8, все это заключено в консоль Windows, основанную на кодовой странице OEM, несовместимой с UTF-8. Затем все это работает в операционной системе, которая внутренне использует UTF-16. Теперь в сочетании с настройкой бета-версии, которая отменяет всю концепцию кодовой базы OEM на уровне ОС, эта настройка обеспечивает достаточно сложность для некоторого непонятного поведения.