Установка кодировки символов Java по умолчанию

362

Как правильно установить кодировку символов по умолчанию, используемую JVM (1.5.x) программно?

Я читал, что -Dfile.encoding=whateverраньше это был путь для старых JVM. У меня нет такой роскоши по причинам, в которые я не пойду.

Я пытался:

System.setProperty("file.encoding", "UTF-8");

И свойство устанавливается, но, похоже, не вызывает последний getBytesвызов ниже использовать UTF8:

System.setProperty("file.encoding", "UTF-8");

byte inbytes[] = new byte[1024];

FileInputStream fis = new FileInputStream("response.txt");
fis.read(inbytes);
FileOutputStream fos = new FileOutputStream("response-2.txt");
String in = new String(inbytes, "UTF8");
fos.write(in.getBytes());
Вилли Ментцель
источник
Отличные комментарии, ребята - и то, о чем я уже думал сам. К сожалению, есть базовый вызов String.getBytes (), который я не могу контролировать. Единственный способ, который я сейчас вижу, - это программно установить кодировку по умолчанию. Любые другие предложения?
6
может быть, неуместный вопрос, но есть ли разница, когда UTF8 установлен с "UTF8", "UTF-8" или "utf8". Недавно я обнаружил, что контейнеры IBM WAS 6.1 EJB и WEB по-разному обрабатывают (с учетом регистра) строки, используемые для определения кодировки.
igor.beslic
5
Просто деталь, но: предпочитаю UTF-8 UTF8 (только первый стандарт). Это все еще применяется в 2012 году ...
Кристоф Русси
4
Установка или чтение file.encodingсвойства не поддерживается .
Макдауэлл
@erickson Мне все еще не ясно с запросом, не правда ли, что file.encoding уместен, когда используются символьные потоки ввода / вывода (все подклассы class Reader& class Writer)? Поскольку class FileInputStreamэто поток ввода-вывода, основанный на байтах, так почему нужно заботиться о наборе символов в потоке ввода-вывода на основе байтов?
сверхобмена

Ответы:

312

К сожалению, file.encodingсвойство должно быть указано при запуске JVM; к моменту ввода вашего основного метода кодировка символов, используемая конструкторами по String.getBytes()умолчанию InputStreamReaderи OutputStreamWriterпостоянно кэшируемая.

Как указывает Эдвард Греч, в особом случае, подобном этому, переменная окружения JAVA_TOOL_OPTIONS может использоваться для указания этого свойства, но обычно это делается так:

java -Dfile.encoding=UTF-8  com.x.Main

Charset.defaultCharset()будет отражать изменения в file.encodingсвойстве, но большая часть кода в основных библиотеках Java, которые должны определять кодировку символов по умолчанию, не использует этот механизм.

Когда вы кодируете или декодируете, вы можете запросить file.encodingсвойство или Charset.defaultCharset()найти текущую кодировку по умолчанию и использовать соответствующий метод или перегрузку конструктора, чтобы указать его.

Эриксон
источник
9
Для полноты я хотел бы добавить, что с небольшой хитростью вы можете перейти к фактически используемой кодировке по умолчанию (как кешируется), благодаря Гари Кронину: byte [] byteArray = {'a'}; InputStream inputStream = new ByteArrayInputStream (byteArray); InputStreamReader reader = new InputStreamReader (inputStream); String defaultEncoding = reader.getEncoding (); lists.xcf.berkeley.edu/lists/advanced-java/1999-O October/…
Стейн де Витт,
2
JDK-4163515 содержит дополнительную информацию о настройке file.encodingsysprop после запуска JVM.
Каспар
2
Я почесал голову, потому что эта команда не работала на Windows, Linux и Mac отлично ... затем я поместил "вокруг значения, как это: java -D" file.encoding = UTF-8 "-jar
cabaji99
проверьте мой ответ в случае Java Spring Boot: stackoverflow.com/a/48952844/986160
Михаил Михайлидис
170

Из документации по интерфейсу инструмента JVM ™

Поскольку к командной строке не всегда можно получить доступ или изменить ее, например, во встроенных виртуальных машинах или просто виртуальных машинах, запущенных глубоко в сценариях, предоставляется JAVA_TOOL_OPTIONSпеременная, чтобы в этих случаях можно было запускать агенты.

Установив (Windows) переменной среды JAVA_TOOL_OPTIONSв -Dfile.encoding=UTF8, то (Java) Systemсвойство устанавливается автоматически каждый раз , когда начинается JVM. Вы будете знать, что параметр был выбран, потому что следующее сообщение будет отправлено System.err:

Picked up JAVA_TOOL_OPTIONS: -Dfile.encoding=UTF8

Эдвард Греч
источник
Знаете ли вы, что утверждение "Подобрано ..." будет напечатано в журналах Tomcat?
thatidiotguy
1
Привет, Эдвард Греч. Благодарю за решение. Это было решено моей проблемой в другом сообщении на форуме. stackoverflow.com/questions/14814230/…
Smaug
8
UTF8или UTF-8?
Tiny
1
@Tiny Java понимает оба. stackoverflow.com/questions/6031877/…
DLight
Ваше решение сэкономило мое время, большое спасибо!
Собхан
67

У меня есть хакерский способ, который определенно работает !!

System.setProperty("file.encoding","UTF-8");
Field charset = Charset.class.getDeclaredField("defaultCharset");
charset.setAccessible(true);
charset.set(null,null);

Таким образом, вы собираетесь обмануть JVM, который подумает, что charset не установлен, и заставит его снова установить его в UTF-8 во время выполнения!

naskoos
источник
2
NoSuchFieldException для меня
SparK
10
Для того, чтобы хак сработал, вы должны предположить, что менеджер безопасности отключен. Если у вас нет способа установить флаг JVM, возможно, у вас (вероятно) также есть система с включенным менеджером безопасности.
Йонатан
3
JDK9 больше не одобряет этот взлом. WARNING: An illegal reflective access operation has occurred • WARNING: Illegal reflective access by [..] • WARNING: Please consider reporting this to the maintainers of [..] • WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations • WARNING: All illegal access operations will be denied in a future release
Дотвин
1
@Enerccio: Это не очень хороший ответ, это грязный взлом, и проблема, ожидающая своего появления. Это следует использовать только в качестве экстренной меры.
Слеське
1
@Enerccio: Можно утверждать, должен ли Java «иметь» способ установить это - можно также утверждать, что разработчики «должны» явно указывать кодировку, когда это уместно. В любом случае, это решение потенциально может вызвать серьезные проблемы в долгосрочной перспективе, следовательно, предостережение «только для экстренного использования». На самом деле даже экстренное использование сомнительно, потому что есть поддерживаемый способ сделать это, установив JAVA_TOOL_OPTIONS, как объяснено в другом ответе.
слеске
38

Я думаю, что лучший подход, чем установка набора символов по умолчанию для платформы, особенно если учесть, что у вас есть ограничения в отношении влияния на развертывание приложения, не говоря уже о платформе, состоит в том, чтобы назвать гораздо более безопасным String.getBytes("charsetName"). Таким образом, ваше приложение не зависит от не зависящих от него вещей.

Лично я считаю, что это String.getBytes()следует считать устаревшим, так как это вызвало серьезные проблемы в ряде случаев, которые я видел, когда разработчик не учитывал возможное изменение кодировки по умолчанию.

Дов Вассерман
источник
18

Я не могу ответить на ваш оригинальный вопрос, но я хотел бы предложить вам несколько советов - не зависите от кодировки JVM по умолчанию. Всегда лучше явно указать желаемую кодировку (то есть "UTF-8") в вашем коде. Таким образом, вы знаете, что он будет работать даже в разных системах и конфигурациях JVM.

Марк Новаковский
источник
7
За исключением, конечно, если вы пишете приложение для настольного компьютера и обрабатываете определенный пользователем текст, который не имеет метаданных кодирования - тогда кодировка платформы по умолчанию является вашим лучшим предположением относительно того, что может использовать пользователь.
Майкл Боргвардт
@MichaelBorgwardt «тогда кодирование платформы по умолчанию является вашим лучшим предположением», вы, похоже, советуете, что изменение по умолчанию - не очень хорошая идея. Вы имеете в виду, использовать везде, где это возможно, явное кодирование, используя предоставленное по умолчанию значение, когда больше ничего не возможно?
Raedwald
1
@Redwald: да, это то, что я имел в виду. Кодировка платформы по умолчанию (по крайней мере на компьютере конечного пользователя) обычно используется пользователями в локали, на которую настроена система. Это информация, которую вы должны использовать, если у вас нет более качественной (т.е. специфичной для документа) информации.
Майкл Боргвардт
1
@MichaelBorgwardt Ерунда. Используйте библиотеку для автоматического определения входной кодировки и сохраните как Unicode с BOM. Это единственный способ справиться с кодовым адом и бороться с ним.
Александр Дубинский
Я думаю, что вы двое не на одной странице. Майкл говорит о декодировании, а Рэдвальд, вы говорите об обработке после декодирования.
WesternGun
12

Попробуй это :

    new OutputStreamWriter( new FileOutputStream("Your_file_fullpath" ),Charset.forName("UTF8"))
Emmanuel.B
источник
5

У нас были те же проблемы. Мы методично попробовали несколько предложений из этой статьи (и других) безрезультатно. Мы также попытались добавить -Dfile.encoding=UTF8и ничего не получалось.

Для людей, которые испытывают эту проблему, в следующей статье , наконец , помогла нам выследить описывает , как региональные настройки могут привести к поломке unicode/UTF-8вJava/Tomcat

http://www.jvmhost.com/articles/locale-breaks-unicode-utf-8-java-tomcat

Правильная настройка локали в ~/.bashrcфайле сработала у нас.

D Яркий
источник
4

Я много чего перепробовал, но пример кода здесь работает отлично. Ссылка на сайт

Суть кода:

String s = "एक गाव में एक किसान";
String out = new String(s.getBytes("UTF-8"), "ISO-8859-1");
Lavixu
источник
4

Если вы используете Spring Boot и хотите передать аргумент file.encodingв JVM, вы должны запустить его так:

mvn spring-boot:run -Drun.jvmArguments="-Dfile.encoding=UTF-8"

это было необходимо для нас, так как мы использовали JTwigшаблоны, и операционная система имела ANSI_X3.4-1968то, что мы узнали черезSystem.out.println(System.getProperty("file.encoding"));

Надеюсь, это поможет кому-то!

Михаил Михайлидис
источник
2

Я использую Amazon (AWS) Elastic Beanstalk и успешно изменил его на UTF-8.

В Elastic Beanstalk выберите Конфигурация> Программное обеспечение, «Свойства среды». Добавьте (имя) JAVA_TOOL_OPTIONS с (значение) -Dfile.encoding = UTF8

После сохранения среда перезапустится с кодировкой UTF-8.

Беренд Меннинга
источник
1

Непонятно, чем вы занимаетесь, и на данный момент у вас нет контроля. Если вы можете вставить другой класс OutputStream в целевой файл, вы можете использовать подтип OutputStream, который преобразует Strings в байты под определенным вами набором символов, скажем UTF-8 по умолчанию. Если модифицированного UTF-8 достаточно для ваших нужд, вы можете использовать DataOutputStream.writeUTF(String):

byte inbytes[] = new byte[1024];
FileInputStream fis = new FileInputStream("response.txt");
fis.read(inbytes);
String in = new String(inbytes, "UTF8");
DataOutputStream out = new DataOutputStream(new FileOutputStream("response-2.txt"));
out.writeUTF(in); // no getBytes() here

Если этот подход неосуществим, может помочь, если вы уточнить здесь, что именно вы можете и не можете контролировать с точки зрения потока данных и среды выполнения (хотя я знаю, что иногда это легче сказать, чем определить). Удачи.

Дов Вассерман
источник
5
DataInputStream и DataOutputStream являются классами специального назначения, которые никогда не должны использоваться с простыми текстовыми файлами. Используемые ими модифицированные UTF-8 несовместимы с реальными UTF-8. Кроме того, если ОП мог использовать ваше решение, он мог бы также использовать правильный инструмент для этой работы: OutputStreamWriter.
Алан Мур
1
mvn clean install -Dfile.encoding=UTF-8 -Dmaven.repo.local=/path-to-m2

Команда работала с exec-maven-plugin для устранения следующей ошибки при настройке задачи jenkins.

Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=512m; support was removed in 8.0
Error occurred during initialization of VM
java.nio.charset.IllegalCharsetNameException: "UTF-8"
    at java.nio.charset.Charset.checkName(Charset.java:315)
    at java.nio.charset.Charset.lookup2(Charset.java:484)
    at java.nio.charset.Charset.lookup(Charset.java:464)
    at java.nio.charset.Charset.defaultCharset(Charset.java:609)
    at sun.nio.cs.StreamEncoder.forOutputStreamWriter(StreamEncoder.java:56)
    at java.io.OutputStreamWriter.<init>(OutputStreamWriter.java:111)
    at java.io.PrintStream.<init>(PrintStream.java:104)
    at java.io.PrintStream.<init>(PrintStream.java:151)
    at java.lang.System.newPrintStream(System.java:1148)
    at java.lang.System.initializeSystemClass(System.java:1192)
прабуши самаракун
источник
0

Мы устанавливаем два системных свойства вместе, и это заставляет систему принимать все в utf8

file.encoding=UTF8
client.encoding.overrideUTF-8
Лизи
источник
7
Кажется, что свойство client.encoding.override специфично для WebSphere.
Кристоф Русси
0

Недавно я столкнулся с системой Notes 6.5 местной компании и обнаружил, что в веб-почте будут отображаться неидентифицируемые символы при установке Windows, не относящейся к Zhongwen. Покопались несколько недель в Интернете, разобрались всего несколько минут назад:

В свойствах Java добавьте следующую строку в Параметры времени выполнения

-Dfile.encoding=MS950 -Duser.language=zh -Duser.country=TW -Dsun.jnu.encoding=MS950

Настройка UTF-8 не будет работать в этом случае.

midmaestro
источник
0

Моя команда столкнулась с той же проблемой на машинах с Windows .. затем удалось решить ее двумя способами:

a) Установить переменную окружения (даже в системных настройках Windows)

JAVA_TOOL_OPTIONS
-Dfile.encoding = UTF8

б) Добавьте следующий фрагмент в ваш pom.xml:

 -Dfile.encoding=UTF-8 

В ПРЕДЕЛАХ

 <jvmArguments>
 -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8001
 -Dfile.encoding=UTF-8
 </jvmArguments>
JacobTheKnitter
источник