2009-08-13 8 views
11

Sto leggendo un documento XML (UTF-8) e alla fine visualizzo il contenuto su una pagina Web utilizzando ISO-8859-1. Come previsto, alcuni caratteri non vengono visualizzati correttamente, ad esempio , e (vengono visualizzati come?).Conversione da UTF-8 a ISO-8859-1 in Java

È possibile convertire questi caratteri da UTF-8 a ISO-8859-1?

Ecco un frammento di codice che ho scritto per tentare questo:

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); 

Io non sono molto sicuro di quello che sta succedendo storto, ma credo che sia readLine() che causa il dolore (dal momento che le corde sarebbe essere codificato in Java/UTF-16?). Un'altra variante che ho provato era di sostituire latin1 con

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

Ho letto i post precedenti sul tema e sto imparando come vado. Grazie in anticipo per il vostro aiuto.

risposta

12

Non sono sicuro che ci sia una routine di normalizzazione nella libreria standard che lo farà. Non penso che la conversione delle virgolette "intelligenti" sia gestita dalle routine standard Unicode normalizer, ma non citatemi.

La cosa intelligente da fare è scaricare ISO-8859-1 e iniziare a utilizzare UTF-8. Detto questo, è possibile codificare qualsiasi punto di codice Unicode normalmente consentito in una pagina HTML codificata come ISO-8859-1. È possibile codificare utilizzando escape sequences come illustrato di seguito:

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; 
    } 
} 

Esempio di utilizzo:

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()); 

Sopra, il carattere a sinistra DOPPIA VIRGOLETTA (U+201C& # x201C;) è codificato come & # x201C ;. Allo stesso modo sono codificati un paio di altri punti di codice arbitrari.

L'attenzione deve essere presa con questo approccio. Se il tuo testo ha bisogno di essere scappato per HTML, è necessario farlo prima che il codice sopra o la e commerciale diventino escape.

+0

funziona benissimo . Grazie! – Chocula

+0

Questo mi ha salvato un sacco di dolore! – daniel0mullins

4

A seconda della codifica predefinita, le righe seguenti potrebbero causare problemi,

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

return new String(latin1); 

In Java, String/Char è sempre in UTF-16 BE. Codifica diversa è coinvolta solo quando converti i caratteri in byte. Supponiamo che la codifica predefinita sia UTF-8, il buffer latin1 sia trattato come UTF-8 e che alcune sequenze di Latin-1 possano formare una sequenza UTF-8 non valida e otterrete?.

1

quando si installa l'oggetto String, è necessario indicare quale codifica utilizzare.

Quindi sostituire:

return new String(latin1); 

da

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

con Java 8, McDowell's answer può essere semplificata come questo (pur conservando corretta gestione delle coppie di surrogati):

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; 
    } 
}