String.getBytes是否被安全使用?

3

目前,我需要在Java中处理字符串的字节,这引发了许多关于编码和JVM实现细节的问题。我想知道我正在做的事情是否有意义,或者它是多余的。

首先,我明白在运行时,Java字符串中的字符始终表示Unicode中的符号。

其次,UTF-8编码始终能够成功地编码Unicode中的任何符号。反过来,以下代码片段将始终返回一个byte[],而不进行任何替换。getBytes文档在此处

byte[] stringBytes = myString.getBytes(StandardCharsets.UTF_8);

然后,如果在另一个JVM实例中以以下方式使用stringBytes,它将始终生成与myString等效的字符串。

new String(stringBytes, StandardCharsets.UTF_8);

您认为我对getBytes的理解正确吗?如果是这样,您如何证明它?我是否遗漏了某些特殊情况,可能导致我无法获得与myString等效的版本?

提前感谢。


编辑:

您是否同意通过以下操作,任何非异常流都会导致处理的情况,这使我们能够成功重构字符串?


编辑:

根据答案,以下是解决方案,允许您在没有抛出异常的情况下安全地重构字符串。您仍然需要以某种方式处理异常。

首先,使用编码器获取字节:

final CharsetEncoder encoder =
    StandardCharsets.UTF_8.
        .newEncoder()
        .onUnmappableCharacter(CodingErrorAction.REPORT)
        .onMalformedInput(CodingErrorAction.REPORT);


// It throws a CharacterCodingException in case there is a replacement or malformed string
// The given array is actually bigger than required because it is the internal array used by the ByteBuffer. Read its doc.
byte[] stringBytes = encoder.encode(CharBuffer.wrap(string)).array();

第二步,使用编码器提供的字节构建字符串(非异常路径):

new String(stringBytes, StandardCharsets.UTF_8);

编辑:我忘记写字符串实例化代码片段了。 - Manuel Carrasco
您的问题中有很多假设是几乎但并非完全正确的。例如“在运行时,Java字符串中的char始终表示Unicode符号”是不正确的,因为String实际上是UTF-16编码的(因为char不能容纳所有可能的Unicode代码点)。如果您想确保可以避免获取byte []的捷径,则可以直接使用CharsetEncoder,在其中可以配置它如何处理格式错误输入(即在发生格式错误时获得通知,而不是将其悄悄转换为替换字符)。 - Joachim Sauer
@JoachimSauer 嗯,从技术上讲,OP 没有说“所有 Unicode 代码点都可以表示为 char”。他们只说“所有 char 表示某些 Unicode 代码点”,据我所知,这是正确的。暗示只能单向传递 :) - Sweeper
@Sweeper:虽然技术上代理值(0xD800-0xDFFF)是Unicode代码点,但这些char值实际上并不代表那些Unicode代码点,只包含了一半信息来找出实际引用的代码点。 - Joachim Sauer
@ManuelCarrasco:自己回答自己的问题是可以的,但请以回答的形式进行。这样更容易被看到,并且也可以单独进行投票/评论。顺便说一句,那也是我会选择的解决方案。 - Generous Badger
显示剩余3条评论
1个回答

2

它将始终产生与myString等效的字符串。

嗯,并非总是如此。这个世界上很少有事情发生“总是”。

我能想到的一个特殊情况是当您调用getBytes时,myString可能是一个“无效”的字符串。例如,它可能有一个单独的代理对:

String myString = "\uD83D";

这种情况发生的频率很大程度上取决于您对myString的操作,所以我会让您自己思考这个问题。
如果myString有一个孤立的代理对,getBytes将为其编码一个问号字符:
// prints "?"
System.out.println(
    new String(myString.getBytes(StandardCharsets.UTF_8), StandardCharsets.UTF_8)
);

我不会说一个?等价于一个格式错误的字符串。

另请参见:Java字符串实例始终有效吗?


我会这样表达:所有以良好编码的String表示的有效Unicode字符串都将正确地往返传输。有时候,为什么给定的String无效并不是很明显。 - Joachim Sauer
谢谢你们两位目前为止的回答。你们认为我编辑后的代码会防止我们出现未处理的情况吗?我很感谢你们提供了一个边缘案例,这确实让我的原始代码失败了。 - Manuel Carrasco
@ManuelCarrasco 这样可以确保不会对格式错误的字符串进行编码,但我建议您以某种方式处理异常。 - Sweeper
谢谢。实际上,我只是想让示例代码保持简短。 - Manuel Carrasco
这就是为什么String::size说它返回“代码点的数量”,因此String s = "";的大小返回2,例如。那个问号(在这种情况下也有抛出异常的方法),是一种告诉你的方式 - 欢迎来到一个应该是代理对的代码点,我不知道该怎么处理这个。 - Eugene

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接