2016-10-02 1 views
2

В настоящее время я разрабатываю приложение, в котором пользователи могут редактировать ByteBuffer через интерфейс шестнадцатеричного редактора, а также редактировать соответствующий текст через JTextPane. Моя текущая проблема заключается в том, что для JTextPane требуется строка, мне нужно преобразовать ByteBuffer в String перед отображением значения. Однако во время преобразования недопустимые символы заменяются символом замены по умолчанию для набора символов. Это выдает недопустимое значение, поэтому, когда я преобразовываю его обратно в байтовый буфер, значение недопустимых символов заменяется байтовым значением символа замены по умолчанию. Есть ли простой способ сохранить байтовое значение недопустимого символа в строке? Я прочитал следующие сообщения stackoverflow, но обычно люди хотят просто заменить непечатаемые символы, мне нужно их сохранить.Проблемы преобразования между ByteBuffer и String в Java

Java ByteBuffer to String

Java: Converting String to and from ByteBuffer and associated problems

Есть простой способ сделать это, или мне нужно следить за всеми изменениями, которые происходят в текстовом редакторе и применить их к ByteBuffer?

Вот код, демонстрирующий проблему. В коде используется байт [] вместо ByteBuffer, но проблема такая же.

 byte[] temp = new byte[16]; 
     // 0x99 isn't a valid UTF-8 Character 
     Arrays.fill(temp,(byte)0x99); 

     System.out.println(Arrays.toString(temp)); 
     // Prints [-103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103] 
     // -103 == 0x99 

     System.out.println(new String(temp)); 
     // Prints ���������������� 
     // � is the default char replacement string 

     // This takes the byte[], converts it to a string, converts it back to a byte[] 
     System.out.println(Arrays.toString(new String(temp).getBytes())); 
     // I need this to print [-103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103] 
     // However, it prints 
     //[-17, -65, -67, -17, -65, -67, -17, -65, -67, -17, -65, -67, -17, -65, -67, -17, -65, -67, -17, -65, -67, -17, -65, -67, -17, -65, -67, -17, -65, -67, -17, -65, -67, -17, -65, -67, -17, -65, -67, -17, -65, -67, -17, -65, -67, -17, -65, -67] 
     // The printed byte is the byte representation of � 
+0

Я думаю, что для этого нужен код. Звучит как ошибка. Также может быть концептуальная ошибка: какая точная текстовая последовательность (ы), с которой вы столкнулись, превращается в байты? – markspace

+0

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

ответ

0

Особенно UTF-8 ошибется

byte[] bytes = {'a', (byte) 0xfd, 'b', (byte) 0xe5, 'c'}; 
    String s = new String(bytes, StandardCharsets.UTF_8); 
    System.out.println("s: " + s); 

Одна нужна CharsetDecoder. Там можно игнорировать (= удалить) или заменить оскорбительные байты или по умолчанию: пусть генерируется исключение.

Для JTextPane мы используем HTML, поэтому мы можем написать шестнадцатеричный код байт-нарушения в <span>, придав ему красный фон.

ByteBuffer byteBuffer = ByteBuffer.wrap(bytes); 
    CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder(); 
    CharBuffer charBuffer = CharBuffer.allocate(bytes.length * 50); 
    charBuffer.append("<html>"); 
    for (;;) { 
     try { 
      CoderResult result = decoder.decode(byteBuffer, charBuffer, false); 
      if (!result.isError()) { 
       break; 
      } 
     } catch (RuntimeException ex) { 
     } 
     int b = 0xFF & byteBuffer.get(); 
     charBuffer.append(String.format(
      "<span style='background-color:red; font-weight:bold'> %02X </span>", 
      b)); 
     decoder.reset(); 
    } 
    charBuffer.rewind(); 
    String t = charBuffer.toString(); 
    System.out.println("t: " + t); 

Код не отражает очень хороший API, но играет с ним.

+0

Это действительно хорошая идея, о которой я даже не подумал. Единственная проблема, с которой я вижу, - это тонна дополнительной разметки, находящейся в тексте JTextPane, когда я буду преобразовывать ее из строки в байт []. У вас есть идеи о том, как обойти это? –

+0

'replaceAll (" <[^>] *> "," ")' или лучше петля с шаблоном Matcher. –

+0

JTextPane также позволяет использовать стилизованный текст (StyledDocument) и использовать атрибуты отдельно от текста, но это громоздко, особенно если вы хотите разрешить редактирование. Но вы можете использовать 'byteBuffer.position()' для отметки этих байтов. –

0

Что Вы думаете о том, что new String(temp).getBytes() сделают с вами?

Я могу сказать вам, что он делает что-то ПЛОХО.

  1. Он преобразует temp в String, используя кодировку по умолчанию, который, вероятно, неправильно, и может привести к потере информации.
  2. Он преобразует результаты обратно в массив байтов, используя кодировку по умолчанию.

Чтобы превратить byte[] в String, вы всегда должны пройти Charset в String конструктор, либо использовать декодер непосредственно. Поскольку вы работаете с буферами, вы можете найти API декодера подходящим.

Чтобы превратить String в byte[], вы всегда должны звонить getBytes(Charset), чтобы вы знали, что используете правильную кодировку.

Основываясь на комментариях, теперь я подозреваю, что ваша проблема заключается в том, что для преобразования из байтов в шестнадцатеричный для вашего пользовательского интерфейса вам нужно написать код следующего типа: (А потом что-то соответствующее, чтобы вернуться.)

String getHexString(byte[] bytes) { 
    StringBuilder builder = new StringBuilder(); 
    for (byte b : bytes) { 
     int nibble = b >> 4; 
     builder.append('0' + nibble); 
     nibble = b & 0xff; 
     builder.append('0' + nibble); 
    } 
    return builder.toString(); 
} 
+0

Я понимаю, что лучшая практика выделяет, что и getBytes, и конструктор String должны брать Charset. Проблема все еще существует, если я передаю Charset в конструктор String. 'new String (temp," UTF-8 ")' выдает исключение 'UnsupportedEncodingException', потому что' byte [] 'содержит несмываемые символы по дизайну. Я чувствую, что ответ должен будет использовать API CharsetDecoder, но я не видел примеров, использующих его для чего-то подобного. –

+0

Если он содержит не-UTF-8, вы не можете преобразовать его в строку, если хотите сохранить всю информацию. Вам нужно преобразовать каждый байт в две шестнадцатеричные цифры; нет никакого способа сделать это с использованием API-интерфейсов, которые вы используете. – bmargulies

+0

@ JustinA.Moore Итак, теперь, когда мы нашли концептуальную ошибку/ошибку, что именно вы хотите делать с неустранимыми символами. * Они по определению не могут быть отобраны, поэтому у вас должен быть план для них вне рамок 'Charset'. – markspace

Смежные вопросы