Как указать значение Java file.encoding, совместимое с базовой кодовой страницей Windows?

У меня есть приложение Java, которое получает данные через сокет, используя InputStreamReader. Он сообщает «Cp1252» из своего метода getEncoding:

/* java.net. */ Socket Sock = ...;
InputStreamReader is = new InputStreamReader(Sock.getInputStream());
System.out.println("Character encoding = " + is.getEncoding());
// Prints "Character encoding = Cp1252"

Это не обязательно соответствует тому, что система сообщает как свою кодовую страницу. Например:

C:\>chcp
Active code page: 850

Приложение может получить байт 0x81, который в кодовой странице 850 представляет символ ü. Программа интерпретирует этот байт с кодовой страницей 1252, которая не определяет ни одного символа в этом значении, поэтому вместо этого я получаю вопросительный знак.

Я смог обойти эту проблему для одного клиента, который использовал кодовую страницу 850, добавив еще один параметр командной строки в пакетный файл, запускающий приложение:

java.exe -Dfile.encoding=Cp850 ...

Но, конечно, не все мои клиенты используют кодовую страницу 850. Как заставить Java использовать кодовую страницу, совместимую с базовой системой Windows? Я бы предпочел что-то, что я мог бы просто поместить в пакетный файл, оставив код Java нетронутым:

ENC=...
java.exe -Dfile.encoding=%ENC% ...

person Rob Kennedy    schedule 26.08.2009    source источник


Ответы (4)


Кодировка по умолчанию, используемая cmd.exe, - это Cp850 (или любая другая "OEM" CP, родная для ОС); системная кодировка - Cp1252 (или любая другая CP "ANSI", родная для ОС). Кровавые подробности здесь. Один из способов узнать кодировку консоли — сделать это через собственный код (см. GetConsoleOutputCP для текущей кодировки консоли; см. GetACP кодировка "ANSI" по умолчанию; и т. д..) .

Изменение кодировки с помощью переключателя -D повлияет на все ваши механизмы кодирования по умолчанию, включая перенаправление stdout/stdin/stderr. Это не идеальное решение.

Я придумал этот сценарий WSH, который может установить консоль на системную кодовую страницу ANSI, но не понял, как программно переключиться на шрифт TrueType.

'file:  setacp.vbs
'usage: cscript /Nologo setacp.vbs
Set objShell = CreateObject("WScript.Shell")
'replace ACP (ANSI) with OEMCP for default console CP
cp = objShell.RegRead("HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001" &_
                              "\Control\Nls\CodePage\ACP")
WScript.Echo "Switching console code page to " & cp
objShell.Exec "chcp.com " & cp

(Это мой первый сценарий WSH, поэтому он может быть ошибочным — я не знаком с разрешениями на чтение реестра.)

Использование шрифта TrueType — еще одно требование для использования ANSI/Unicode с cmd.exe. Я собираюсь посмотреть на программный переход на лучший шрифт, когда позволит время.

person McDowell    schedule 26.08.2009

Что касается фрагмента кода, правильный ответ — использовать подходящий конструктор для InputStreamReader, выполняющий правильное преобразование кода. Таким образом, не имеет значения, какая кодировка используется по умолчанию в системе, вы знаете, что получаете правильную кодировку, соответствующую тому, что вы получаете в сокете.

Затем вы можете указать кодировку при записи файлов, если вам нужно, вместо того, чтобы полагаться на системную кодировку, но, конечно, когда они открывают файлы в этой системе, у них могут возникнуть проблемы, но современные системы Windows поддерживают UTF-8, поэтому вы можете записать файл в UTF-8, если вам нужно (внутренне Java представляет все строки как 16-битный юникод).

Я бы подумал, что это «правильное» решение в целом, которое было бы наиболее совместимым с большим количеством базовых систем.

person Yishai    schedule 26.08.2009
comment
+1. Кстати, в моей системе Windows 7 активной кодовой страницей является 850, но Java сообщает Cp1252 как системное свойство file.encoding. - person Vinay Sajip; 26.08.2009
comment
Клиенты и сервер должны быть настроены с использованием одной и той же кодировки, какой бы она ни была для любого клиента. Приложение, отличное от Java, отправляет символьные данные на сервер, используя локальную кодовую страницу, сервер сохраняет данные, а затем сервер отправляет их в приложение Java. Никто не хранит кодовую страницу, потому что пока все используют одну и ту же, это не имеет значения. Проблема в том, что приложение Java не взаимодействует; он всегда использует Cp1252. (Правильное решение — изменить протокол, чтобы заставить все работать, скажем, в UTF-8, но изменение протокола нарушает все существующие установки.) - person Rob Kennedy; 27.08.2009
comment
Тогда похоже, что у G_A есть ответ. Другой вариант — заставить это не-java-приложение сообщать вашему java-приложению, что, по его мнению, является кодировкой, а затем использовать соответствующий конструктор, как описано выше. - person Yishai; 27.08.2009

В Windows есть дополнительные сложности, связанные с наличием двух активных кодовых страниц. В вашем примере верны и 1252, и 850, но они зависят от того, как запускается программа. Для приложений с графическим интерфейсом Windows будет использовать кодовую страницу ANSI, которая для западноевропейских языков обычно равна 1252. Однако в командной строке будет указана кодовая страница OEM, которая равна 850 для тех же языковых стандартов.

person ferdley    schedule 26.08.2009
comment
Вы сделали верные заявления, но я не уверен, как они отвечают на мой вопрос. Очевидно, что программа Java должна быть совместима с кодовой страницей OEM. Итак, как мне выбрать значение file.encoding на основе этого? Программа запускается через java.exe. - person Rob Kennedy; 27.08.2009

Если значение кодовой страницы, возвращаемое командой chcp, возвращает нужное вам значение, вы можете использовать следующую команду, чтобы получить кодовую страницу

C:\>for /F "Tokens=4" %I in ('chcp') Do Set CodePage=%I

Это устанавливает переменную CodePage в значение кодовой страницы, возвращенное из chcp.

C:\>echo %CodePage%
437

Вы можете использовать это значение в своем файле bat, добавив к нему префикс Cp.

C:\>echo Cp%CodePage%
Cp437

Если вы поместите это в bat-файл, значения %I в первой команде необходимо будет заменить на %%I

person GregA100k    schedule 26.08.2009
comment
Это кажется многообещающим, но оно основано на определенных предположениях о формате вывода chcp, который может отличаться в системах, отличных от английского. Например, в немецком языке кодовая страница находится в токене 3, а после числа стоит точка: Активная кодовая страница: 850. - person Rob Kennedy; 27.08.2009
comment
Вот как это работает даже для немецкой системы: FOR /F "TOKENS=2 DELIMS=:." %%I IN ('chcp') DO SET cp=%%I, затем обрезать пробелы с помощью set cp=%cp: =% и, наконец, echo Cp%cp% - person fubar; 31.10.2014