byte bytes[] = new byte[16];
random.nextBytes(bytes);
try {
return new String(bytes, "UTF-8");
} catch (UnsupportedEncodingException e) {
log.warn("Hash generation failed", e);
}
当我使用给定方法生成String时,当我应用string.getBytes().length
时,它返回一些其他值。 Max为32.为什么16字节数组最终生成另一个大小的字节字符串?
但如果我做string.length()
则返回16。
答案 0 :(得分:5)
这是因为您的 bytes 首先转换为Unicode字符串,该字符串尝试从这些字节创建UTF-8 char 序列。如果一个字节不能被视为ASCII字符,也不能被下一个字节捕获以形成合法的unicode字符,则它将替换为“ ”。调用String#getBytes()
时,此类字符将转换为3个字节,从而为结果输出添加2个额外字节。
如果你很幸运只生成ASCII字符,String#getBytes()
将返回16字节数组,否则,结果数组可能会更长。例如,以下代码段:
byte[] b = new byte[16];
Arrays.fill(b, (byte) 190);
b = new String(b, "UTF-8").getBytes();
返回48(!)字节长的数组。
答案 1 :(得分:3)
生成的字节可能包含有效的多字节字符。
以此为例。该字符串只包含一个字符,但作为字节表示,它需要三个字节。
String s = "Ω";
System.out.println("length = " + s.length());
System.out.println("bytes = " + Arrays.toString(s.getBytes("UTF-8")));
String.length()
以字符形式返回字符串的长度。字符Ω
是一个字符,而它是UTF-8中的3字节长。
如果你改变你的代码
Random random = new Random();
byte bytes[] = new byte[16];
random.nextBytes(bytes);
System.out.println("string = " + new String(bytes, "UTF-8").length());
System.out.println("string = " + new String(bytes, "ISO-8859-1").length());
使用不同的字符集解释相同的字节。并遵循String(byte[] b, String charset)
The length of the new String is a function of the charset, and hence may
not be equal to the length of the byte array.
答案 2 :(得分:3)
由于对byte
和char
s之间关系的误解而产生的经典错误,所以我们再来一次。
byte
和char
之间没有1对1的映射关系;这一切都取决于你使用的字符编码(在Java中,这是一个Charset
)。
更糟糕的是:给定byte
序列,或不可以编码为char
序列。
试试这个例子:
final byte[] buf = new byte[16];
new Random().nextBytes(buf);
final Charset utf8 = StandardCharsets.UTF_8;
final CharsetDecoder decoder = utf8.newDecoder()
.onMalformedInput(CodingErrorAction.REPORT);
decoder.decode(ByteBuffer.wrap(buf));
这很可能抛出MalformedInputException
。
我知道这不是答案,但是你没有清楚地解释你的问题;并且上面的示例已经表明您对byte
与char
的内容之间存在错误的理解。
答案 3 :(得分:1)
如果查看您正在生成的字符串,您生成的大多数随机字节都不会形成有效的UTF-8字符。因此,String
构造函数将其替换为unicode' REPLACEMENT CHARACTER' �,占用3个字节,0xFFFD。
举个例子:
public static void main(String[] args) throws UnsupportedEncodingException
{
Random random = new Random();
byte bytes[] = new byte[16];
random.nextBytes(bytes);
printBytes(bytes);
final String s = new String(bytes, "UTF-8");
System.out.println(s);
printCharacters(s);
}
private static void printBytes(byte[] bytes)
{
for (byte aByte : bytes)
{
System.out.print(
Integer.toHexString(Byte.toUnsignedInt(aByte)) + " ");
}
System.out.println();
}
private static void printCharacters(String s)
{
s.codePoints().forEach(i -> System.out.println(Character.getName(i)));
}
在给定的运行中,我得到了这个输出:
30 41 9b ff 32 f5 38 ec ef 16 23 4a 54 26 cd 8c 0A��2�8��#JT&͌ DIGIT ZERO LATIN CAPITAL LETTER A REPLACEMENT CHARACTER REPLACEMENT CHARACTER DIGIT TWO REPLACEMENT CHARACTER DIGIT EIGHT REPLACEMENT CHARACTER REPLACEMENT CHARACTER SYNCHRONOUS IDLE NUMBER SIGN LATIN CAPITAL LETTER J LATIN CAPITAL LETTER T AMPERSAND COMBINING ALMOST EQUAL TO ABOVE
答案 4 :(得分:0)
String.getBytes()。length 可能更长,因为它计算表示字符串所需的字节数,而length()计算2字节代码单位。
了解更多here
答案 5 :(得分:0)
这将尝试创建一个字符串,假设字节是UTF-8。
new String(bytes, "UTF-8");
这通常会出现严重错误,因为UTF-8多字节序列可能无效。
像:
String s = new String(new byte[] { -128 }, StandardCharsets.UTF_8);
第二步:
byte[] bytes = s.getBytes();
将使用平台编码(System.getProperty("file.encoding")
)。更好地指定它。
byte[] bytes = s.getBytes(StandardCharsets.UTF_8);
应该意识到,内部String将维护Unicode,即UTF-16中的16位char
数组。
应该完全放弃对String
使用byte[]
。它总是涉及转换,成本双倍内存并且容易出错。