Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/357.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 为什么UTF-8的新字符串包含更多字节_Java_String_Byte - Fatal编程技术网

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