字节数组到字符串并返回。.与 -127 有关的问题

2022-09-01 08:07:33

在以下:

 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

[编辑:]下面的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)

因此,总结一下:在编码和解码之间进行转换。如果要将二进制数据放在字符串中,则必须在字符级别进行操作。但是,请注意,这将为您提供一个垃圾字符串(即结果不会像预期的那样格式正确,UTF-16),因此您最好将其读出为字符并将其转换回字节。StringArray[Byte]String

您可以将字节向上移动,例如,添加512;然后你会得到一堆有效的单代码点。但这是使用16位来表示每8位,编码效率为50%。Base64 是序列化二进制数据的更好选择(8 位表示 6,效率为 75%)。Char


答案 2

字符串用于存储文本而不是二进制数据。

在默认字符编码中,-127 没有字符,因此它将其替换为“?”或63。

编辑: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.");

推荐