2009-06-08 7 views
9

Si consideri il seguente codice:gestione dei valori di surrogati Unicode in stringhe Java

byte aBytes[] = { (byte)0xff,0x01,0,0, 
        (byte)0xd9,(byte)0x65, 
        (byte)0x03,(byte)0x04, (byte)0x05, (byte)0x06, (byte)0x07, 
        (byte)0x17,(byte)0x33, (byte)0x74, (byte)0x6f, 
        0, 1, 2, 3, 4, 5, 
        0 }; 
String sCompressedBytes = new String(aBytes, "UTF-16"); 
for (int i=0; i<sCompressedBytes.length; i++) { 
    System.out.println(Integer.toHexString(sCompressedBytes.codePointAt(i))); 
} 

Ottiene il seguente output corretto:

ff01, 0, fffd, 506, 717, 3374, 6f00, 102, 304, 500. 

Tuttavia, se il 0xd9 nei dati di input viene modificato in 0x9d, poi si ottiene la seguente uscita corretta:

ff01, 0, 9d65, 304, 506, 717, 3374, 6f00, 102, 304, 500. 

I reali ze che la funzionalità è dovuta al fatto che il byte 0xd9 è un marcatore Unicode ad alto surrogato.

Domanda: C'è un modo per alimentare, identificare ed estrarre i byte surrogati (0xd800 a 0xdfff) in una stringa Java Unicode?
Grazie

risposta

4

C'è un modo per alimentare, identificare ed estrarre byte surrogati (0xD800 a 0xdfff) in una stringa Java Unicode ?

Solo perché nessuno lo ha menzionato, farò notare che la classe Character include i metodi per lavorare con coppie surrogate. Per esempio. isHighSurrogate(char), codePointAt(CharSequence, int) e toChars(int). Mi rendo conto che questo è oltre il punto del problema dichiarato.

new String(aBytes, "UTF-16"); 

Questa è un'operazione di decodifica che trasformerà i dati di input.Sono abbastanza sicuro che non sia legale perché l'operazione di decodifica scelta richiede che l'input inizi con 0xfe 0xff o 0xff 0xfe (il byte order mark). Inoltre, non tutti i valori di byte possibili possono essere decodificati correttamente perché UTF-16 è un variable width encoding.

Se si voleva una trasformazione simmetrica di byte arbitrari String e indietro, si sono meglio con una codifica a singolo byte a 8 bit per ogni valore byte è un carattere valido:

Charset iso8859_15 = Charset.forName("ISO-8859-15"); 
byte[] data = new byte[256]; 
for (int i = Byte.MIN_VALUE; i <= Byte.MAX_VALUE; i++) { 
    data[i - Byte.MIN_VALUE] = (byte) i; 
} 
String asString = new String(data, iso8859_15); 
byte[] encoded = asString.getBytes(iso8859_15); 
System.out.println(Arrays.equals(data, encoded)); 

Nota: il numero di caratteri equivale al numero di byte (raddoppiando la dimensione dei dati); la stringa risultante non sarà necessariamente stampabile (contenente come potrebbe, a bunch of control characters).

Sono with Jon, anche se - mettendo sequenze di byte arbitrarie in stringhe Java è quasi sempre una cattiva idea.

10

EDIT: Questo affronta la questione dal commento

Se si vuole codificare i dati binari arbitrari in una stringa, si dovrebbe non utilizzare una codifica del testo normale. Non hai un testo valido in quella codifica: hai solo dati binari arbitrari.

Base64 è il modo per andare qui. Non c'è supporto di base64 direttamente in Java (in una classe pubblica, comunque) ma ci sono varie librerie di terze parti che è possibile utilizzare, come ad esempio the one in the Apache Commons Codec library.

Sì, base64 aumenterà la dimensione dei dati, ma consentirà di decodificarlo in un secondo momento senza perdere informazioni.

EDIT: Questo affronta la questione originale

Credo che il problema è che non è stata specificata una corretta surrogato coppia. È necessario specificare byte che rappresentano un surrogato basso e quindi un surrogato elevato. Dopodiché, dovresti essere in grado di aggiungere il punto di codice appropriato. Nel tuo caso, hai dato un surrogato basso da solo.

Ecco il codice per dimostrare questo:

public class Test 
{ 
    public static void main(String[] args) 
     throws Exception // Just for simplicity 
    { 
     byte[] data = 
     { 
      0, 0x41, // A 
      (byte) 0xD8, 1, // High surrogate 
      (byte) 0xDC, 2, // Low surrogate 
      0, 0x42, // B 
     }; 

     String text = new String(data, "UTF-16"); 

     System.out.printf("%x\r\n", text.codePointAt(0)); 
     System.out.printf("%x\r\n", text.codePointAt(1)); 
     // Code point at 2 is part of the surrogate pair 
     System.out.printf("%x\r\n", text.codePointAt(3));  
    } 
} 

uscita:

41 
10402 
42 
+0

Credo che tu abbia ragione. Ero appena giunto alla stessa conclusione ma ho controllato per vedere se qualcuno più esperto avesse già risposto. –

+0

Semplicemente inserendo "(byte) 0xDC, (byte) 0xEF," rese "FF01 694ef dcef ...", che è come dovrebbe essere. –

+0

Grazie per le vostre risposte. Ma il problema non riguarda l'incorporamento di caratteri surrogati. Il requisito è di alimentare qualsiasi sequenza di byte arbitraria (che viene emessa dalla compressione) in una stringa Java e di leggerla come una sequenza di byte equivalente. –