2009-06-08 8 views
9

Considérez le code suivant:Gestion des valeurs de substitution Unicode dans les chaînes 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))); 
} 

Obtient la sortie incorrecte suivante:

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

Cependant, si le 0xd9 dans les données d'entrée est changé en 0x9d, puis la sortie correcte suivante est obtenue:

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

I reali ze que la fonctionnalité est due au fait que l'octet 0xd9 est un marqueur Unicode de substitution élevée. Question: Existe-t-il un moyen de fournir, identifier et extraire des octets de substitution (0xd800 à 0xdfff) dans une chaîne Java Unicode?
Merci

Répondre

4

est-il un moyen de nourrir, d'identifier et d'extraire les octets de substitution (0xD800 à 0xdfff) dans une chaîne Java Unicode ?

Juste parce que personne ne l'a mentionné, je ferai remarquer que la classe Character inclut les méthodes pour travailler avec des paires de substitution. Par exemple. isHighSurrogate(char), codePointAt(CharSequence, int) et toChars(int). Je réalise que c'est d'ailleurs le point du problème énoncé.

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

Ceci est une opération de décodage qui va transformer les données d'entrée.Je suis assez sûr que ce n'est pas légal parce que l'opération de décodage choisie exige que l'entrée commence par 0xfe 0xff ou 0xff 0xfe (le byte order mark). De plus, toutes les valeurs d'octets possibles ne peuvent pas être décodées correctement car UTF-16 est un variable width encoding.

Si vous voulez une transformation symétrique d'octets arbitraires à cordes et à l'arrière, vous êtes mieux avec un 8 bits, le codage sur un seul octet parce que chaque valeur d'octet est un caractère valide:

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

Note: le nombre de caractères va égaler le nombre d'octets (doublant la taille des données); la chaîne résultante ne sera pas nécessairement imprimable (contenant comme il se peut, un bunch of control characters).

Je suis with Jon, mais mettre des séquences d'octets arbitraires dans des chaînes Java est presque toujours une mauvaise idée.

10

EDIT: Cette aborde la question du commentaire

Si vous souhaitez encoder des données binaires arbitraires dans une chaîne, vous devez pas utiliser un encodage de texte normal. Vous n'avez pas de texte valide dans cet encodage - vous avez juste des données binaires arbitraires.

Base64 est le chemin à parcourir ici. Il n'y a pas de support base64 directement dans Java (dans une classe publique, de toute façon) mais il y a plusieurs bibliothèques tierces que vous pouvez utiliser, telles que the one in the Apache Commons Codec library.

Oui, base64 va augmenter la taille des données - mais cela vous permettra de le décoder plus tard sans perdre d'informations.

EDIT: Cette adresse la question initiale

Je crois que le problème est que vous n'avez pas spécifié une bonne paire de substitution . Vous devez spécifier des octets représentant un substitut faible, puis un substitut élevé. Après cela, vous devriez pouvoir ajouter le point de code approprié. Dans votre cas, vous avez donné une mère porteuse faible.

Code est ici pour démontrer:

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

Sortie:

41 
10402 
42 
+0

Je crois que vous avez raison. Je venais de parvenir à la même conclusion, mais je suis retourné pour voir si quelqu'un de plus compétent avait déjà répondu. –

+0

simplement d'insérer "(octet) 0xdc, (octet) 0xEF," rendements "FF01 694ef DCEF ..." Ce qui est comme il devrait être. –

+0

Merci pour vos réponses. Mais, le problème ne concerne pas l'incorporation de caractères de remplacement. L'exigence consiste à alimenter toute séquence arbitraire d'octets (qui proviennent de la compression) en une chaîne Java et à la lire comme une séquence d'octets équivalente. –

Questions connexes