Обработка суррогата Unicode оценивает в строках Java

Чтобы дать вам очень простой пример:

Допустим, у вас есть строка "123". Соответствующие символы имеют ^ внизу в следующих примерах.

  1. Regex: \d+?. частичное совпадение!

    123  # The \d+? eats only 1 because he's lazy (on a diet) and leaves the 2 to the .(dot).
    ^^   # This means \d+? eats as little as possible.
    
  2. Regex: \d+. полное совпадение!

    123  # The \d+ eats 12 and leaves the 3 to the .(dot).
    ^^^  # This means \d+ is greedy but can still share some of his potential food to his neighbour friends.
    
  3. Регулярное выражение: \d++. нет совпадения!

    123  # The \d++ eats 123. He would even eat more if there were more numbers following. 
         # This means \d++ is possessive. There is nothing left over for the .(dot), so the pattern can't be matched.
    
9
задан hippietrail 6 April 2011 в 13:57
поделиться

2 ответа

Есть ли способ кормления, идентифицировать и извлекать суррогатные байты (от 0xd800 до 0xdfff) в строке Java Unicode?

Просто потому, что никто не упомянул об этом, я отмечу, что класс Character включает методы для работы с суррогатными парами . Например, isHighSurrogate (char) , codePointAt (CharSequence, int) и toChars (int) . Я понимаю, что это не только суть заявленной проблемы.

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

Это операция декодирования, которая преобразует входные данные. Я почти уверен, что это незаконно, потому что выбранная операция декодирования требует, чтобы ввод начинался либо с 0xfe 0xff, либо с 0xff 0xfe (отметка порядка байтов ). К тому же, не каждое возможное значение байта может быть декодировано правильно, потому что UTF-16 - это кодировка переменной ширины .

Если вам нужно симметричное преобразование произвольных байтов в String и обратно, вам лучше использовать 8- бит, однобайтовая кодировка, потому что каждое значение байта является допустимым символом:

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

Примечание: количество символов будет равно количеству байтов (удвоение размера данных); результирующая строка не обязательно будет печатаемой (содержащая, как бы то ни было, набор управляющих символов ).

Я с Джоном , хотя - помещаю произвольный байт последовательности в строки Java - почти всегда плохая идея.

Если вам нужно симметричное преобразование произвольных байтов в String и обратно, вам лучше использовать 8-битную однобайтовую кодировку, потому что каждое значение байта является допустимым символом:

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

Примечание: количество символов равно будет равным количеству байтов (удвоение размера данных); результирующая строка не обязательно будет печатаемой (содержащая, как бы то ни было, набор управляющих символов ).

Я с Джоном , хотя - помещаю произвольный байт последовательности в строки Java - почти всегда плохая идея.

Если вам нужно симметричное преобразование произвольных байтов в String и обратно, вам лучше использовать 8-битную однобайтовую кодировку, потому что каждое значение байта является допустимым символом:

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

Примечание: количество символов равно будет равным количеству байтов (удвоение размера данных); результирующая строка не обязательно будет печатаемой (содержащая, как бы то ни было, набор управляющих символов ).

Я с Джоном , хотя - помещаю произвольный байт последовательности в строки Java - почти всегда плохая идея.

4
ответ дан 4 December 2019 в 14:31
поделиться

РЕДАКТИРОВАТЬ: Это отвечает на вопрос из комментария

. Если вы хотите кодировать произвольные двоичные данные в строке, вам следует , а не использовать обычную кодировку текста. У вас нет действительного текста в этой кодировке - у вас есть просто произвольные двоичные данные.

Base64 - это то, что вам нужно. Непосредственно в Java нет поддержки base64 (во всяком случае, в общедоступном классе), но вы можете использовать различные сторонние библиотеки, например библиотеку кодеков Apache Commons .

Да, base64 будет увеличьте размер данных, но это позволит вам декодировать их позже, не теряя информации.

РЕДАКТИРОВАТЬ: Это решает исходный вопрос

Я считаю, что проблема в том, что вы не указали правильный суррогат пара . Вы должны указать байты, представляющие младший суррогат, а затем высокий суррогат. После этого вы сможете добавить соответствующую кодовую точку. В вашем случае вы сами задали низкий суррогат.

Вот код, чтобы продемонстрировать это:

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

Вывод:

41
10402
42
10
ответ дан 4 December 2019 в 14:31
поделиться
Другие вопросы по тегам:

Похожие вопросы: