martes, enero 08, 2013

Encoding en Java


En un artículo anterior comenté someramente los "errores cometidos por obviar" más habituales en el desarrollo informático. Ahora me centraré en los detalles relacionados con la codificación de caracteres en el mundo Java.
Máquina virtual
La máquina virtual (JVM) internamente maneja UTF-16 para la representación interna del texto, tanto para String, como para char. O, al menos, así era originalmente.


En ciertas versiones modernas, si arrancamos la JVM con -XX:+UserCompressedStrings (que puede ir por defecto activada en algunas versiones 6 o superior), se guardarán los caracteres como 8 bit (byte) codificado como ISO-8859-1, mientras no necesiten algún otro caracter especial. Además, añade la serialización de cadenas con una modificación del UTF-8, vamos, poco recomendable.
No siempre es facil predecir la codificación por defecto que empleará la JVM durante su ejecución. Muchísimos recursos por internet se dedican a desentrañar tales misterios, y en algunos lo complican aún más.
En muchas aplicaciones Java se emplea el comando System.out.println para volcar por consola algún texto. Pero la codificación de la salida es dependiente de la instalación, los valores por defecto,...
Si queremos estar seguros de la codificación de la salida por consola, hay que encapsular el System.out dentro de un PrintStream:

PrintStream ps=null;
try
{
  ps=new PrintStream( System.out, true, "utf-8");
}
catch(UnsupportedEncodingException e)
{
  System.out.println("Codificacion desconocida");
  ps=System.out;
}
ps.println("Salida con ñ y \u20AC");

La API estandadr de Java esta diseñada para los ficheros de properties esten codificados como ISO-8859-1. Los caracteres que necesitemos introducir que no entren en dicho juego de caracteres, habrá que escaparlos como código Unicode: \unnnn . Hay plugins para Eclipse que permiten escribir los properties normal, y el traduciendo por debajo al código Unicode.
Alrededor de Java
Proyectos
Por defecto, al compilar java, el sistema supondrá que el código fuente estará codificado en la codificación por defecto del sistema (ya hemos comentado antes que esto no es siempre previsible). Pero se lo podemos indicar con la opción -encoding encoding. Pudiendo así incluir textos en idiomas menos habituales. En un fichero ANT, sería así:

<javac srcdir="${src.java.dir}"
     destdir="${build.dest}"
     encoding="UTF-8"
     debug="${debug}"
     deprecation="${deprecation}"
     optimize="${optimize}"
     classpathref="build.classpath"/>

Pero hay que tener una precaución. Por ejemplo, en UTF8 no hay marca de orden de bytes (BOM), pero el "bloc de notas de Windows", y algunas otras aplicaciones, al guardar un texto codificado en utf8, incluirá al principio del fichero dicho BOM, lo cual es innecesario, y lo que es peor, no le gusta al compilador javac.
De este modo, podemos incluir cualquier tipo de caracter en los comentarios, identificadores,... y al emplear javadoc, se generará la documentación internacionalizada.
En javadoc, tenemos 3 opciones:
  1. -encoding para indicar la codificación de los ficheros fuente de entrada (igual que para javac)
  2. -docencoding especifica la codificación de salida
  3. -charset para indicar la información de codificación en la metadata de salida
log4j 
Casi todas las aplicaciones java, y más si son de servidor, incluyen un sistema de registro, que suele emplear la biblioteca log4j. La codificación de la salida que genere dicha biblioteca se puede modificar.
Si empleamos un fichero properties para ello, el método sería:

org4j.appender.R=org.apache.log4j.DailyRollingFileAppender
log4j.appender.R.encoding=UTF-8
Pero si lo configuramos mediante fichero XML, sería:

<appender name="R"
             class="org.apache.log4j.DailyRollingFileAppender">
       <param name="File" value="info-log.log"/>
       <param name="Encoding" value="UTF-8"/>
       ...
       ...
</appender>

1 comentario:

Marcos Pérez dijo...

Continuando con el tema de las obviedades olvidadas, he publicado un artículo sobre Java y las fechas-hora: Java - Fechas y horas