Байт-массив для String и обратно.. проблемы с -127

В следующем:

 scala> (new String(Array[Byte](1, 2, 3, -1, -2, -127))).getBytes
 res12: Array[Byte] = Array(1, 2, 3, -1, -2, 63)

почему -127 преобразован в 63? и как мне вернуть его как -127

[EDIT:] Java-версия ниже (чтобы показать, что это не просто проблема "Scala" )

c:\tmp>type Main.java
public class Main {
    public static void main(String [] args) {
        byte [] b = {1, 2, 3, -1, -2, -127};
        byte [] c = new String(b).getBytes();
        for (int i = 0; i < 6; i++){
            System.out.println("b:"+b[i]+"; c:"+c[i]);
        }
    }
}
c:\tmp>javac Main.java
c:\tmp>java Main
b:1; c:1
b:2; c:2
b:3; c:3
b:-1; c:-1
b:-2; c:-2
b:-127; c:63

Ответ 1

Конструктор, который вы вызываете, делает неочевидным, что преобразование с двоичной строкой использует декодирование: String(byte[] bytes, Charset charset). Вы хотите использовать декодирование вообще.

К счастью, для этого есть конструктор: String(char[] value).

Теперь у вас есть данные в строке, но вы хотите вернуть ее точно так же, как есть. Но угадай, что! getBytes(Charset charset) Правильно, там также применяется автоматическое кодирование. К счастью, существует метод toCharArray().

Если вы должны начинать с байтов и заканчивать байтами, вам необходимо сопоставить массивы char с байтами:

(new String(Array[Byte](1,2,3,-1,-2,-127).map(_.toChar))).toCharArray.map(_.toByte)

Итак, суммируем: преобразование между String и Array[Byte] включает кодирование и декодирование. Если вы хотите поместить двоичные данные в строку, вы должны сделать это на уровне символов. Обратите внимание, однако, что это даст вам строку мусора (т.е. Результат не будет хорошо сформирован UTF-16, поскольку ожидается String), и поэтому вам лучше прочитать его как символы и преобразовать его назад к байтам.

Вы можете сдвинуть байты, скажем, добавив 512; то вы получите кучу действительных одиночных кодовых пунктов Char. Но это использует 16 бит для представления эффективности 8, 50% эффективности кодирования. Base64 - лучший вариант для сериализации двоичных данных (8 бит для представления 6, 75% эффективности).

Ответ 2

Строка предназначена для хранения текстовых двоичных данных.

В вашей кодировке по умолчанию отсутствует charcter для -127, поэтому она заменяет его на?? или 63.

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

Чтобы ответить на ваш вопрос буквально, вы можете использовать свою собственную кодировку символов. Это очень плохая идея, так как любой текст, вероятно, будет закодирован и искажен так же, как вы видели. Использование Base64 позволяет избежать этого, используя символы, которые безопасны в любой кодировке.

byte[] bytes = new byte[256];
for (int i = 0; i < bytes.length; i++)
    bytes[i] = (byte) i;
String text = new String(bytes, 0);
byte[] bytes2 = new byte[text.length()];
for (int i = 0; i < bytes2.length; i++)
    bytes2[i] = (byte) text.charAt(i);
int count = 0;
for (int i = 0; i < bytes2.length; i++)
    if (bytes2[i] != (byte) i)
        System.out.println(i);
    else
        count++;
System.out.println(count + " bytes matched.");

Ответ 4

Используйте правильную кодировку:

scala> (new String(Array[Byte](1, 2, 3, -1, -2, -127), "utf-16")).getBytes("utf-16")
res13: Array[Byte] = Array(-2, -1, 1, 2, 3, -1, -2, -127)