Establecer la codificación de caracteres Java predeterminada

362

¿Cómo configuro correctamente la codificación de caracteres predeterminada utilizada por JVM (1.5.x) mediante programación?

He leído que -Dfile.encoding=whateversolía ser el camino a seguir para las JVM más antiguas. No tengo ese lujo por razones en las que no me involucraré.

Yo he tratado:

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

Y la propiedad se establece, pero no parece causar que la última getBytesllamada a continuación use 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());
Willi Mentzel
fuente
Excelentes comentarios chicos, y cosas en las que ya estaba pensando. Desafortunadamente, hay una llamada String.getBytes () subyacente sobre la que no tengo control. La única forma en que actualmente veo para evitarlo es establecer la codificación predeterminada mediante programación. ¿Cualquier otra sugerencia?
66
quizás sea una pregunta irrelevante, pero, ¿hay alguna diferencia cuando UTF8 está configurado con "UTF8", "UTF-8" o "utf8". Recientemente descubrí que los contenedores IBM WAS 6.1 EJB y WEB tratan de manera diferente las cadenas (en forma de mayúsculas y minúsculas) utilizadas para definir la codificación.
igor.beslic
55
Solo un detalle pero: prefiera UTF-8 a UTF8 (solo el primero es estándar). Esto todavía se aplica en 2012 ...
Christophe Roussy
44
Establecer o leer la file.encodingpropiedad no es compatible .
McDowell
@erickson Todavía no estoy claro con la consulta. ¿No es cierto que "file.encoding" es relevante cuando se utilizan secuencias de E / S basadas en caracteres (todas las subclases de class Reader& class Writer)? Debido a que class FileInputStreames un flujo de E / S basado en bytes, entonces ¿por qué uno debería preocuparse por el conjunto de caracteres en el flujo de E / S basado en bytes?
intercambio excesivo el

Respuestas:

312

Desafortunadamente, la file.encodingpropiedad tiene que especificarse cuando se inicia la JVM; en el momento en que se ingresa su método principal, la codificación de caracteres utilizada por String.getBytes()y los constructores predeterminados de InputStreamReadery OutputStreamWriterse ha almacenado en caché de forma permanente.

Como señala Edward Grech, en un caso especial como este, la variable de entorno JAVA_TOOL_OPTIONS se puede usar para especificar esta propiedad, pero normalmente se hace así:

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

Charset.defaultCharset()reflejará los cambios en la file.encodingpropiedad, pero la mayoría del código en las bibliotecas principales de Java que necesitan determinar la codificación de caracteres predeterminada no utiliza este mecanismo.

Cuando está codificando o decodificando, puede consultar la file.encodingpropiedad o Charset.defaultCharset()encontrar la codificación predeterminada actual y utilizar el método apropiado o la sobrecarga del constructor para especificarla.

erickson
fuente
99
Para completar, me gustaría agregar que con un poco de truco puede obtener la codificación predeterminada realmente utilizada (como está en caché), gracias a Gary Cronin: byte [] byteArray = {'a'}; InputStream inputStream = nuevo ByteArrayInputStream (byteArray); InputStreamReader reader = new InputStreamReader (inputStream); Cadena defaultEncoding = reader.getEncoding (); lists.xcf.berkeley.edu/lists/advanced-java/1999-October/…
Stijn de Witt
2
JDK-4163515 tiene más información sobre cómo configurar file.encodingsysprop después del inicio de JVM.
Caspar
2
Me estaba rascando la cabeza porque ese comando no funcionaba perfectamente en Windows, Linux y Mac ... luego puse "alrededor del valor de esta manera: java -D" file.encoding = UTF-8 "-jar
cabaji99
compruebe mi respuesta en caso de Java Spring Boot: stackoverflow.com/a/48952844/986160
Michail Michailidis
170

De la documentación de la interfaz de la herramienta JVM ™ ...

Dado que no siempre se puede acceder o modificar la línea de comandos, por ejemplo, en máquinas virtuales integradas o simplemente máquinas virtuales lanzadas dentro de scripts, JAVA_TOOL_OPTIONSse proporciona una variable para que los agentes se puedan iniciar en estos casos.

Al establecer la variable de entorno (Windows) JAVA_TOOL_OPTIONSen-Dfile.encoding=UTF8 , la Systempropiedad (Java) se establecerá automáticamente cada vez que se inicie una JVM. Sabrá que el parámetro se ha seleccionado porque se publicará el siguiente mensaje en System.err:

Picked up JAVA_TOOL_OPTIONS: -Dfile.encoding=UTF8

Edward Grech
fuente
¿Sabe que la declaración "Recogido ..." se imprimiría en los registros de Tomcat?
thatidiotguy
1
Hola Edward Grech, gracias por tu solución. Se resolvió mi problema en otra publicación del foro. stackoverflow.com/questions/14814230/…
Smaug
8
UTF8o UTF-8?
Diminuto
1
@Tiny Java comprende ambos. stackoverflow.com/questions/6031877/…
DLight
Su solución me ahorró tiempo, ¡muchas gracias!
Sobhan
67

¡Tengo una manera hacky que definitivamente funciona!

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

De esta manera, engañará a JVM, que pensaría que el conjunto de caracteres no está configurado y lo configurará nuevamente en UTF-8, en tiempo de ejecución.

naskoos
fuente
2
NoSuchFieldException para mí
SparK
10
Para que el truco funcione, debes asumir que el administrador de seguridad está desactivado. Si no tiene una manera de configurar un indicador JVM, es posible que (probablemente) también tenga un sistema habilitado para el administrador de seguridad.
Yonatan
3
JDK9 ya no aprueba este truco. 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
dotwin
1
@Enerccio: Esa no es una buena respuesta, es un truco sucio y un problema a la espera de que suceda. Eso solo debe usarse como una medida de emergencia.
sleske
1
@Enerccio: es discutible si Java "debería" tener una forma de configurar esto; también se podría argumentar que los desarrolladores "deberían" especificar explícitamente la codificación siempre que sea relevante. En cualquier caso, esta solución tiene el potencial de causar serios problemas a largo plazo, de ahí la advertencia de "solo para uso de emergencia". En realidad, el uso de emergencia, aún es cuestionable, ya que es una manera de hacerlo con el apoyo, el establecimiento de JAVA_TOOL_OPTIONS como se explica en otra respuesta.
sleske
38

Creo que un mejor enfoque que establecer el conjunto de caracteres predeterminado de la plataforma, especialmente porque parece tener restricciones para afectar el despliegue de la aplicación, y mucho menos la plataforma, es llamar a los más seguros String.getBytes("charsetName") . De esa forma, su aplicación no depende de cosas que escapan a su control.

Personalmente, creo que String.getBytes()debe ser desaprobado, ya que ha causado serios problemas en una serie de casos que he visto, en los que el desarrollador no tuvo en cuenta el posible cambio del juego de caracteres predeterminado.

Dov Wasserman
fuente
18

No puedo responder a su pregunta original, pero me gustaría ofrecerle algunos consejos: no dependa de la codificación predeterminada de la JVM. Siempre es mejor especificar explícitamente la codificación deseada (es decir, "UTF-8") en su código. De esa manera, sabrá que funcionará incluso en diferentes sistemas y configuraciones JVM.

Marc Novakowski
fuente
77
Excepto, por supuesto, si está escribiendo una aplicación de escritorio y procesando un texto especificado por el usuario que no tiene metadatos de codificación, entonces la codificación predeterminada de la plataforma es su mejor estimación de lo que el usuario podría estar usando.
Michael Borgwardt
@MichaelBorgwardt "entonces la codificación predeterminada de la plataforma es su mejor conjetura" parece estar aconsejando que querer cambiar el valor predeterminado no sea una buena idea. ¿Quiere decir, utilizar una codificación explícita siempre que sea posible, utilizando el valor predeterminado suministrado cuando nada más es posible?
Raedwald
1
@ Raedwald: sí, a eso me refería. La codificación predeterminada de la plataforma es (al menos en una máquina de usuario final) lo que los usuarios en el entorno local en el que está configurado el sistema suelen estar utilizando. Esa es información que debe usar si no tiene información mejor (es decir, específica del documento).
Michael Borgwardt
1
@MichaelBorgwardt Tonterías. Use una biblioteca para detectar automáticamente la codificación de entrada y guárdela como Unicode con BOM. Esa es la única forma de lidiar y luchar contra el infierno de codificación.
Aleksandr Dubinsky
Creo que ustedes dos no están en la misma página. Michael habla sobre la decodificación, mientras que Raedwald habla sobre el procesamiento después de la decodificación.
WesternGun
12

Prueba esto :

    new OutputStreamWriter( new FileOutputStream("Your_file_fullpath" ),Charset.forName("UTF8"))
Emmanuel.B
fuente
5

Teníamos los mismos problemas. Probamos metódicamente varias sugerencias de este artículo (y otras) en vano. También intentamos agregar el -Dfile.encoding=UTF8y nada parecía estar funcionando.

Para las personas que están teniendo este problema, el siguiente artículo, finalmente, ayudó a localizar describe cómo la configuración local puede romper unicode/UTF-8enJava/Tomcat

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

Establecer la configuración regional correctamente en el ~/.bashrcarchivo funcionó para nosotros.

D brillante
fuente
4

He intentado muchas cosas, pero el código de muestra aquí funciona perfecto. Enlace

El quid del código es:

String s = "एक गाव में एक किसान";
String out = new String(s.getBytes("UTF-8"), "ISO-8859-1");
Lavixu
fuente
4

En caso de que esté utilizando Spring Boot y quiera pasar el argumento file.encodingen JVM, debe ejecutarlo así:

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

esto era necesario para nosotros ya que estábamos usando JTwigplantillas y el sistema operativo teníaANSI_X3.4-1968 que descubrimos a través deSystem.out.println(System.getProperty("file.encoding"));

¡Espero que esto ayude a alguien!

Michail Michailidis
fuente
2

Estoy usando Amazon (AWS) Elastic Beanstalk y lo cambié con éxito a UTF-8.

En Elastic Beanstalk, vaya a Configuración> Software, "Propiedades del entorno". Agregue (nombre) JAVA_TOOL_OPTIONS con (valor) -Dfile.encoding = UTF8

Después de guardar, el entorno se reiniciará con la codificación UTF-8.

Berend Menninga
fuente
1

No está claro lo que haces y no tienes control sobre esto en este momento. Si puede interponer una clase OutputStream diferente en el archivo de destino, podría usar un subtipo de OutputStream que convierta las cadenas en bytes bajo un conjunto de caracteres que defina, por ejemplo, UTF-8. Si el UTF-8 modificado es suficiente para sus necesidades, puede usar 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

Si este enfoque no es factible, puede ayudar si aclara aquí exactamente lo que puede y no puede controlar en términos de flujo de datos y entorno de ejecución (aunque sé que a veces es más fácil decirlo que determinarlo). Buena suerte.

Dov Wasserman
fuente
55
DataInputStream y DataOutputStream son clases de propósito especial que nunca deben usarse con archivos de texto sin formato. El UTF-8 modificado que emplean no es compatible con el UTF-8 real. Además, si el OP podría usar su solución, también podría usar la herramienta adecuada para este trabajo: un OutputStreamWriter.
Alan Moore
1
mvn clean install -Dfile.encoding=UTF-8 -Dmaven.repo.local=/path-to-m2

El comando trabajó con exec-maven-plugin para resolver el siguiente error al configurar una tarea 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)
prabushi samarakoon
fuente
0

Establecimos dos propiedades del sistema juntas y hace que el sistema tome todo en utf8

file.encoding=UTF8
client.encoding.overrideUTF-8
lizi
fuente
77
La propiedad client.encoding.override parece ser específica de WebSphere.
Christophe Roussy
0

Recientemente me topé con el sistema Notes 6.5 de una empresa local y descubrí que el correo web mostraría caracteres no identificables en una instalación de Windows no localizada en Zhongwen. He cavado durante varias semanas en línea, lo descubrí hace unos minutos:

En las propiedades de Java, agregue la siguiente cadena a los parámetros de tiempo de ejecución

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

La configuración de UTF-8 no funcionaría en este caso.

midmaestro
fuente
0

Mi equipo encontró el mismo problema en máquinas con Windows ... luego logró resolverlo de dos maneras:

a) Establecer la variable de entorno (incluso en las preferencias del sistema de Windows)

JAVA_TOOL_OPTIONS
-Dfile.encoding = UTF8

b) Introduce el siguiente fragmento en tu pom.xml:

 -Dfile.encoding=UTF-8 

DENTRO

 <jvmArguments>
 -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8001
 -Dfile.encoding=UTF-8
 </jvmArguments>
JacobTheKnitter
fuente