Java 为什么UTF-8的新字符串包含更多字节
当我用给定的方法生成一个字符串时,当我应用Java 为什么UTF-8的新字符串包含更多字节,java,string,byte,Java,String,Byte,当我用给定的方法生成一个字符串时,当我应用String.getBytes().length时,它会返回一些其他值。马克斯32岁。为什么16字节数组最终生成另一个大小的字节字符串 但是如果执行string.length()操作,则返回16。生成的字节可能包含有效的多字节字符 以此为例,。字符串仅包含一个字符,但作为字节表示,它需要三个字节 byte bytes[] = new byte[16]; random.nextBytes(bytes); try { return new String
String.getBytes().length
时,它会返回一些其他值。马克斯32岁。为什么16字节数组最终生成另一个大小的字节字符串
但是如果执行
string.length()
操作,则返回16。生成的字节可能包含有效的多字节字符
以此为例,。字符串仅包含一个字符,但作为字节表示,它需要三个字节
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.length()
返回字符串的长度(以字符为单位)。字符Ω
是一个字符,而在UTF-8中是一个3字节长的字符
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));
如果您像这样更改代码
String s = "Ω";
System.out.println("length = " + s.length());
System.out.println("bytes = " + Arrays.toString(s.getBytes("UTF-8")));
使用不同的字符集解释相同的字节。并从String(byte[]b,String字符集)中的javadoc后面
这是因为您的字节首先转换为Unicode字符串,该字符串尝试从这些字节创建UTF-8字符序列。如果一个字节不能被视为ASCII字符,也不能被下一个字节捕获以形成合法的unicode字符,则将其替换为“�". 调用String#getBytes()
时,这样的字符被转换成3个字节,从而在结果输出中增加2个额外字节
如果您幸运地只生成ASCII字符,String#getBytes()
将返回16字节数组,如果不是,则生成的数组可能更长。例如,以下代码段:
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.
返回长度为48(!)字节的数组。
更多阅读经典错误源于对byte
s和char
s之间关系的误解,所以我们再来一遍
在字节
和字符
之间没有1对1的映射;这完全取决于您使用的字符编码(在Java中,这是字符集
)
更糟糕的是:给定一个字节
序列,它可能会也可能不会被编码成字符
序列
例如,尝试以下方法:
byte[] b = new byte[16];
Arrays.fill(b, (byte) 190);
b = new String(b, "UTF-8").getBytes();
这很可能引发格式错误的InputException
我知道这不完全是一个答案,但是你没有清楚地解释你的问题;上面的例子已经表明你对什么是byte
和什么是char
有错误的理解。这将尝试创建一个字符串,假设字节是UTF-8
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));
这通常会出现可怕的错误,因为UTF-8多字节序列可能无效
比如:
第二步:
String s = new String(new byte[] { -128 }, StandardCharsets.UTF_8);
将使用平台编码(System.getProperty(“file.encoding”)
),最好指定它
byte[] bytes = s.getBytes();
人们应该意识到,字符串将在内部维护Unicode,一个UTF-16中的16位char
数组
对于byte[],应该完全避免使用String
。它将始终涉及转换,占用双倍内存,并且容易出错。如果您查看正在生成的字符串,您正在生成的大多数随机字节不会形成有效的UTF-8字符。字符串
构造函数因此会将它们替换为unicode“替换字符”�, 它占用3个字节,0xFFFD
例如:
byte[] bytes = s.getBytes(StandardCharsets.UTF_8);
在给定的运行中,我得到以下输出:
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&͌
零位数
拉丁文大写字母A
替换字符
替换字符
第二位
替换字符
第八位
替换字符
替换字符
同步空闲
数字符号
拉丁文大写字母J
拉丁文大写字母T
符号
结合几乎等于以上
请尝试几次。或者尝试“string.getBytes().length”而不是“string.length()”等等,你想做什么?你在字节和字符之间混淆了;两者之间没有1对1的映射。这看起来像是XY问题,请解释你想做什么。但我将字节[]转换为字符串。然后将字符串转换回字节[].?您的答案令人困惑。“如果您幸运地只生成ASCII字符,其中一个字节映射到一个字符,结果将是32。”这毫无意义。如果您创建一个包含以下ASCII值的字节数组[108、111、118、101、108、121、32、32、67、65、70、69、66、65、69]
,并用拉丁语创建一个新字符串(new string>)(b,“ISO-8859-1”).getBytes().length
)或UTF-8(新字符串(b,“UTF-8”).getBytes().length
)都有16个字节或16个字符的长度(…ToCharray().length
也许值得一提的是,0xFFFD
(两个字节)是替换字符的代码点�代码>和它的UTF-8表示是EF BF BD
(三个字节)。更多信息可以在维基百科中找到。
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