2013-11-10 9 views
6

Heyho,Encode byte [] as String

Я хочу преобразовать данные байта, которые могут быть любыми, в строку. Мой вопрос, является ли он «безопасный» для кодирования данных байт с UTF-8, например:

String s1 = new String(data, "UTF-8"); 

или с использованием base64:

String s2 = Base64.encodeToString(data, false); //migbase64 

Я просто боюсь, что с помощью первого метод имеет отрицательные побочные эффекты. Я имею в виду, что оба варианта работают p̶e̶r̶f̶e̶c̶t̶l̶y̶, но s1 может содержать любой символ кодировки UTF-8, s2 использует только «читаемые» символы. Я просто не уверен, действительно ли нужно использовать base64. В принципе, мне просто нужно создать String, отправить его по сети и получить его снова. (В моей ситуации нет другого способа: /)

Вопрос только в отрицательных побочных эффектах, если это возможно!

+1

Вам действительно нужно сделать «java.lang.String» вне ваших данных? Почему вы не можете напрямую обращаться с байтовой последовательностью? –

+0

У этого есть технические причины: D Я просто говорю «Minecraft»:/ – maxammann

+0

Я не знал, что Minecraft требует символов! Тем не менее, в отношении вашего вопроса о том, как вы можете потерять данные, причина в том, что недопустимые последовательности UTF-8 будут закодированы в качестве замещающих символов. Я не уверен, правильно ли это ответили ниже. –

ответ

15

Вы должны абсолютно использовать base64 или возможно hex. (Любой из них будет работать, base64 более компактный, но труднее для людей читать.)

Вы утверждаете, что «оба варианта работают отлично», но это на самом деле не так. Если вы используете первый подход, а data на самом деле не является действительной последовательностью UTF-8, вы потеряете данные. Вы не, пытающийся преобразовать текст в кодировке UTF-8 в String, поэтому не пишите код, который пытается это сделать.

Использование ISO-8859-1 в качестве кодировки сохранит все данные, но в очень многих случаях возвращаемая строка не будет легко переноситься по другим протоколам. Например, он может содержать непечатаемые управляющие символы.

использовать только String(byte[], String) конструктор, когда у вас есть по своей сути текстуального данные, которые вам посчастливилось иметь в закодированном виде (где кодировка задается в качестве второго аргумента). Для чего-либо еще - музыки, видео, изображений, зашифрованных или сжатых данных, например, вы должны использовать подход, который обрабатывает входящие данные как «произвольные двоичные данные» и находит текстовое кодирование ... что именно является базовым и hex делать.

+0

Даже если байт [] действителен, вы все равно можете использовать данные. Это связано с тем, что для каждого символа существует одна уникальная кодировка. например Java может использовать 1 байт для '\ 0', но он предпочитает использовать 2. –

+1

@PeterLawrey: я не понимаю ваше первое предложение вообще или как оно относится ко второму ... –

+0

kk очень хорошее answere, единственное я не понимаю, как я могу потерять данные. Удаляет ли java байты, если они недействительны UTF-8? – maxammann

4

Вы можете сохранить байт в строке, хотя это не очень хорошая идея. Вы не можете использовать UTF-8, так как это приведет к смене байтов, но более быстрый и эффективный способ - использовать кодировку ISO-8859-1 или простой 8-разрядный. Самый простой способ сделать это состоит в использовании

String s1 = new String(data, 0); 

или

String s1 = new String(data, "ISO-8859-1"); 

От UTF-8 on Wikipedia, Как Джон Скит ноты, эти кодировки не действуют в соответствии со стандартом. Их поведение в Java меняется. DataInputStream рассматривает их как одно и то же для первых трех версий, а следующие два бросают исключение. Декодер Charset обрабатывает их как отдельные символы молча.

00000000 is \0 
11000000 10000000 is \0 
11100000 10000000 10000000 is \0 
11110000 10000000 10000000 10000000 is \0 
11111000 10000000 10000000 10000000 10000000 is \0 
11111100 10000000 10000000 10000000 10000000 10000000 is \0 

Это означает, что если вы видите \ 0 в вас строку, вы не имеете никакого способа знать наверняка, что исходные байты [] значения были. DataOutputStream использует второй вариант для совместимости с C, который видит \ 0 в качестве терминатора.

BTW DataOutputStream не знает о кодовых точках, поэтому записывает символы с высоким кодом в UTF-16, а затем кодировку UTF-8.

0xFE и 0xFF не действительны, чтобы отображаться в символе. Значения 0x11000000 + могут появляться только в начале символа, а не внутри многобайтового символа.

+1

k спасибо, теперь все ясно, мне хотелось бы ответить на оба ответа: D – maxammann

+0

В чем разница между тем, что «0» - что я не знаком, - и стандартным подходом «ISO-8859-1»? Является ли первое сокращением для последнего? – javadba

+0

@javadba ISO-8859-1 будет кодировать неподдерживаемые символы как '?', Тогда как если вы просто возьмете младшие 8 бит, вы, скорее всего, получите несколько случайный характер. –

2

Подтвержден принятый ответ с Java. Для повторения UTF-8, UTF-16 не сохраняют все байтовые значения. ISO-8859-1 сохраняет все байтовые значения. Но если кодированные байты должны быть перенесены за пределы JVM, используйте Base64.

@Test 
public void testBase64() { 
    final byte[] original = enumerate(); 
    final String encoded = Base64.encodeBase64String(original); 
    final byte[] decoded = Base64.decodeBase64(encoded); 
    assertTrue("Base64 preserves bytes", Arrays.equals(original, decoded)); 
} 

@Test 
public void testIso8859() { 
    final byte[] original = enumerate(); 
    String s = new String(original, StandardCharsets.ISO_8859_1); 
    final byte[] decoded = s.getBytes(StandardCharsets.ISO_8859_1); 
    assertTrue("ISO-8859-1 preserves bytes", Arrays.equals(original, decoded)); 
} 

@Test 
public void testUtf16() { 
    final byte[] original = enumerate(); 
    String s = new String(original, StandardCharsets.UTF_16); 
    final byte[] decoded = s.getBytes(StandardCharsets.UTF_16); 
    assertFalse("UTF-16 does not preserve bytes", Arrays.equals(original, decoded)); 
} 

@Test 
public void testUtf8() { 
    final byte[] original = enumerate(); 
    String s = new String(original, StandardCharsets.UTF_8); 
    final byte[] decoded = s.getBytes(StandardCharsets.UTF_8); 
    assertFalse("UTF-8 does not preserve bytes", Arrays.equals(original, decoded)); 
} 

@Test 
public void testEnumerate() { 
    final Set<Byte> byteSet = new HashSet<>(); 
    final byte[] bytes = enumerate(); 
    for (byte b : bytes) { 
     byteSet.add(b); 
    } 
    assertEquals("Expecting 256 distinct values of byte.", 256, byteSet.size()); 
} 

/** 
* Enumerates all the byte values. 
*/ 
private byte[] enumerate() { 
    final int length = Byte.MAX_VALUE - Byte.MIN_VALUE + 1; 
    final byte[] bytes = new byte[length]; 
    for (int i = 0; i < length; i++) { 
     bytes[i] = (byte)(i + Byte.MIN_VALUE); 
    } 
    return bytes; 
}