2009-08-13 15 views
11

Estoy leyendo un documento XML (UTF-8) y finalmente visualizo el contenido en una página web utilizando ISO-8859-1. Como era de esperar, hay algunos caracteres que no se muestran correctamente, como , y (¿se muestran como?).Conversión de UTF-8 a ISO-8859-1 en Java

¿Es posible convertir estos caracteres de UTF-8 a ISO-8859-1?

Aquí es un fragmento de código que he escrito para intentar esto:

BufferedReader br = new BufferedReader(new InputStreamReader(urlConnection.getInputStream(), "UTF-8")); 
StringBuilder sb = new StringBuilder(); 

String line = null; 
while ((line = br.readLine()) != null) { 
    sb.append(line); 
} 
br.close(); 

byte[] latin1 = sb.toString().getBytes("ISO-8859-1"); 

return new String(latin1); 

No estoy muy seguro de lo que está pasando mal, pero yo creo que es readLine() que está causando el dolor (ya que las cadenas haría ¿Está codificado Java/UTF-16?). Otra variación que probé fue reemplazar latin1 con

byte[] latin1 = new String(sb.toString().getBytes("UTF-8")).getBytes("ISO-8859-1"); 

He leído publicaciones anteriores sobre el tema y estoy aprendiendo sobre la marcha. Gracias de antemano por tu ayuda.

Respuesta

12

No estoy seguro de si hay una rutina de normalización en la biblioteca estándar que hará esto. No creo que la conversión de las cotizaciones "inteligentes" sea manejada por las rutinas estándar Unicode normalizer, pero no me cites.

Lo más inteligente que hacer es volcar ISO-8859-1 y comenzar a usar UTF-8. Dicho esto, es posible codificar cualquier punto de código Unicode normalmente permitido en una página HTML codificada como ISO-8859-1. Puede codificar utilizando escape sequences como se muestra aquí:

public final class HtmlEncoder { 
    private HtmlEncoder() {} 

    public static <T extends Appendable> T escapeNonLatin(CharSequence sequence, 
     T out) throws java.io.IOException { 
    for (int i = 0; i < sequence.length(); i++) { 
     char ch = sequence.charAt(i); 
     if (Character.UnicodeBlock.of(ch) == Character.UnicodeBlock.BASIC_LATIN) { 
     out.append(ch); 
     } else { 
     int codepoint = Character.codePointAt(sequence, i); 
     // handle supplementary range chars 
     i += Character.charCount(codepoint) - 1; 
     // emit entity 
     out.append("&#x"); 
     out.append(Integer.toHexString(codepoint)); 
     out.append(";"); 
     } 
    } 
    return out; 
    } 
} 

Ejemplo de uso:

String foo = "This is Cyrillic Ya: \u044F\n" 
    + "This is fraktur G: \uD835\uDD0A\n" + "This is a smart quote: \u201C"; 

StringBuilder sb = HtmlEncoder.escapeNonLatin(foo, new StringBuilder()); 
System.out.println(sb.toString()); 

Por encima, el carácter IZQUIERDA comilla doble (U+201C& # x201C;) se codifica como & # x201C ; Un par de otros puntos de código arbitrarios también están codificados.

Se debe tener cuidado con este enfoque. Si su texto necesita ser escapado para HTML, eso debe hacerse antes de que el código anterior o los ampersands terminen siendo escapados.

+0

funciona de maravilla . ¡Gracias! – Chocula

+0

¡Esto me salvó un montón de dolor! – daniel0mullins

4

Dependiendo de su codificación por defecto, las líneas siguientes podrían causar un problema,

byte[] latin1 = sb.toString().getBytes("ISO-8859-1"); 

return new String(latin1); 

En Java, Cadena/Char está siempre en UTF-16BE. La codificación diferente solo está involucrada cuando convierte los caracteres a bytes. Supongamos que su codificación predeterminada es UTF-8, el buffer latin1 se trata como UTF-8 y alguna secuencia de Latin-1 puede formar una secuencia UTF-8 no válida y obtendrá?

1

cuando instale su objeto String, debe indicar qué codificación usar.

Así reemplazar:

return new String(latin1); 

por

return new String(latin1, "ISO-8859-1"); 
1

Con Java 8, McDowell's answer se puede simplificar así (preservando al mismo tiempo el manejo correcto de los pares suplentes):

public final class HtmlEncoder { 
    private HtmlEncoder() { 
    } 

    public static <T extends Appendable> T escapeNonLatin(CharSequence sequence, 
                  T out) throws java.io.IOException { 
     for (PrimitiveIterator.OfInt iterator = sequence.codePoints().iterator(); iterator.hasNext();) { 
      int codePoint = iterator.nextInt(); 
      if (Character.UnicodeBlock.of(codePoint) == Character.UnicodeBlock.BASIC_LATIN) { 
       out.append((char) codePoint); 
      } else { 
       out.append("&#x"); 
       out.append(Integer.toHexString(codePoint)); 
       out.append(";"); 
      } 
     } 
     return out; 
    } 
}