将String转换为UTF-8字节数组会在Java中返回负值

mar*_*did 2 java arrays string encoding utf-8

假设我有一个字节数组,我尝试使用以下代码将其编码为UTF_8

String tekst = new String(result2, StandardCharsets.UTF_8);
System.out.println(tekst);
//where result2 is the byte array
Run Code Online (Sandbox Code Playgroud)

然后,我使用getBytes()获取字节,值为0到128

byte[] orig = tekst.getBytes();
Run Code Online (Sandbox Code Playgroud)

然后,我希望使用ff对我的byte [] orig进行频率计数:

int frequencies = new int[256];

for (byte b: orig){
    frequencies[b]++;
}
Run Code Online (Sandbox Code Playgroud)

一切顺利,直到我遇到一个错误

java.lang.ArrayIndexOutOfBoundsException: -61
Run Code Online (Sandbox Code Playgroud)

这是否意味着我的字节仍包含负值,尽管将其转换为UTF-8?我有什么不对劲吗?有人可以告诉我这个原因的清晰度我仍然是这个主题的初学者.谢谢.

Jon*_*eet 7

回答具体问题

这是否意味着我的字节仍包含负值,尽管将其转换为UTF-8?

是的,一点没错.那是因为byte用Java签名.一byte的-61值是195为无符号值.当您使用UTF-8编码任何非ASCII文本时,您应该期望得到不在0-127范围内的字节.

修复很简单:只需用位掩码将范围钳位到0-255:

frequencies[b & 0xff]++;
Run Code Online (Sandbox Code Playgroud)

解决你想要做的事情

这一行:

String tekst = new String(result2, StandardCharsets.UTF_8);
Run Code Online (Sandbox Code Playgroud)

......只有result2真正的UTF-8编码文本才合适.这是合适的话result2是一些任意的二进制数据,如图像,压缩后的数据,或者甚至文本以一些其它编码进行编码.

如果要将任意二进制数据保存为字符串,则应使用Base64或hex之类的内容.基本上,您需要确定您的数据是否具有固有的文本性(在这种情况下,您应该尽可能多地使用字符串,并Charset在必要时使用适当的转换为二进制)或本质上是二进制的(在这种情况下,您应该尽可能多地使用字节,并在必要时使用base64或hex转换为文本.

这一行:

byte[] orig = tekst.getBytes();
Run Code Online (Sandbox Code Playgroud)

...几乎总是一个坏主意.它使用platform-default编码将字符串转换为字节.如果你真的,真的想使用平台默认编码,我会明确地说:

byte[] orig = tekst.getBytes(Charset.defaultCharset());
Run Code Online (Sandbox Code Playgroud)

......但是现在这是一个非常不寻常的要求.在任何地方坚持使用UTF-8几乎总是更好.