2009-06-08 12 views
9

Rozważmy następujący kod:Obchodzenie się wartości zastępcze Unicode w ciągi 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))); 
} 

Pobiera następujące nieprawidłowe wyjście:

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

Jednakże, jeśli 0xd9 w danych wejściowych zmienia się na 0x9d, a następnie uzyskano następujące prawidłowe wyniki:

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

I reali ze ta funkcjonalność wynika z faktu, że bajt 0xd9 jest znacznikiem surogatowym Unicode.

Pytanie: Czy istnieje sposób na podawanie, identyfikowanie i wyodrębnianie bajtów zastępczych (0xd800 do 0xdfff) w ciągu znaków Unicode Java?
Dzięki

Odpowiedz

4

Czy istnieje sposób, aby karmić, zidentyfikować i wyodrębnić zastępczych bajtów (0xd800 do 0xdfff) w ciąg Java Unicode ?

Tylko dlatego, że nikt o tym nie wspomniał, zwrócę uwagę, że klasa Character zawiera metody pracy z zastępczymi parami. Na przykład. isHighSurrogate(char), codePointAt(CharSequence, int) i toChars(int). Zdaję sobie sprawę, że jest to poza kwestią określonego problemu.

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

Jest to operacja dekodowania, która przekształci dane wejściowe.Jestem prawie pewien, że nie jest to legalne, ponieważ wybrana operacja dekodowania wymaga, aby wejście rozpoczynało się od 0xfe 0xff lub 0xff 0xfe (byte order mark). Ponadto nie każda możliwa wartość bajtu może zostać poprawnie zdekodowana, ponieważ UTF-16 to variable width encoding.

Jeśli chciał symetryczną transformację dowolnych bajtów do String iz powrotem, jesteś lepiej z 8-bitowym kodowaniem jednobajtowych ponieważ każda wartość bajt jest ważny znak:

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

Uwaga: liczba znaków będzie równa liczbie bajtów (podwojenie rozmiaru danych); wynikowy ciąg niekoniecznie będzie możliwy do wydrukowania (zawierający, jak to może być, bunch of control characters).

Jestem jednak with Jon, choć - umieszczanie arbitralnych sekwencji bajtowych w ciągach Java jest prawie zawsze złym pomysłem.

10

EDIT: To odnosi się do kwestii z komentarzem

Jeśli chcesz kodować dowolne dane binarne w ciąg, należy nie użyć zwykłego kodowanie tekstu. Nie masz prawidłowego tekstu w tym kodowaniu - masz tylko arbitralne dane binarne.

Base64 to sposób, aby przejść tutaj. W Javie nie ma bezpośrednio obsługi bazowej (w klasie publicznej), ale istnieją różne biblioteki innych firm, z których można korzystać, na przykład the one in the Apache Commons Codec library.

Tak, base64 zwiększy rozmiar danych, ale pozwoli Ci to później rozszyfrować bez utraty informacji.

EDIT: To odnosi się do oryginalnego pytanie

Uważam, że problemem jest to, że nie określono prawidłowego surogat parę. Powinieneś określić bajty reprezentujące niski surogat, a następnie wysoki zastępczy. Następnie powinieneś być w stanie wydłużyć odpowiedni punkt kodowy. W twoim przypadku sam podałeś niski surogat.

Oto kod, aby wykazać to:

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

wyjściowa:

41 
10402 
42 
+0

Uważam, że masz rację. Właśnie doszedłem do tego samego wniosku, ale sprawdziłem, czy ktoś jeszcze bardziej kompetentny już odpowiedział. –

+0

Po prostu wstawiając "(bajt) 0xdc, (bajt) 0xef," daje "ff01 694ef dcef ..." Tak jak być powinno. –

+0

Dziękuję za odpowiedzi. Ale problem nie polega na osadzaniu zastępczych postaci. Wymagane jest przekazywanie dowolnych sekwencji bajtów (które są wyprowadzane z kompresji) do ciągu Java i odczytywanie go jako równoważnej sekwencji bajtów. –

Powiązane problemy