微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

当我尝试获取字符串的字节但从字符到字节的转换溢出整数长度时会发生什么?

如何解决当我尝试获取字符串的字节但从字符到字节的转换溢出整数长度时会发生什么?

给定一个长度为 Integer.MAX_VALUE 的字符串,其中包含需要超过一个字节来表示的字符,例如汉字,如果我执行 String.getBytes() 会得到什么结果?有没有什么好的方法可以测试这种类型的错误

解决方法

基于似乎是 source code for the JRE String class 的内容,它调用 StringCoding class 中的“编码”方法,该方法计算给定字符串所需的最大字节数,并将结果返回内部。请参阅调用 'scale' 的 'encode' 方法。

因此,根据确切的结果,您将得到字符串截断(如果结果为正)或完全失败(如果结果为负)。由于我没有将逻辑深入到 ArrayEncoder 类中,因此在转换过程中可能还会出现“数组索引越界”异常。

(链接是互联网上的一些随机源代码副本,可能不是当前代码)。

这大概只是理论上的兴趣——一个有 20 亿个字符的字符串不太可能表现得很好。

,

String 是一个复杂的不可变类。从历史上看,它只保存 char[] UTF-16 两字节字符数组。然后 String.getBytes(StandardCharsets.UTF_8) 可能确实被假定为溢出索引范围。

然而现在 String 已经包含一个 byte[] value。这是用于压缩其他字符集中的字符串。问题仍然存在,例如几乎 Integer.MAX_VALUE 的压缩 ISO-8859-1 字符串可以在 UTF-8 中爆炸(即使使用 String.toCharArray())。 OutOfMemoryException

因此可能存在一些不同的溢出,但对于 UTF16 字符到 getBytes(UTF-8):

private static final int MAX_INDEX = Integer.MAX_VALUE;

void checkUtf8Bytes(String s) {
    if (s.length() < MAX_INDEX / 6) {
        return; // Not hurt by UTF-8 6 byte sequences.
    }
    if (s.codePoints().mapToLong(this::bytesNeeded).sum() > MAX_INDEX) {
        throw IllegalArgumentException();
    }
}

private int bytesNeeded(int codePoint) {
    if (codePoint < 128) {
        return 1;
    } else if (codePoint ...) {
    ...
}

我认为捕获 OutOfMemoryException 更容易。

请注意,字节中包含 UTF-16 字符的普通字符串不能容纳超过 Integer.MAX_VALUE / 2 个字节。

,

我要问你的问题是如何想出这样的字符串。我找不到构建那么大的字符串的方法。我尝试的一切都给了我一个错误,如:

Exception in thread "main" java.lang.OutOfMemoryError: Requested array size exceeds VM limit

我能找到的由两字节字符组成的最长字符串的大小(以字节为单位)略低于 Integer.MAX_VALUE。我是通过以下方式做到的:

String foo = "\uD83D".repeat((Integer.MAX_VALUE)/2-1);

为您提供一个由 1073741822 字符或 2147483644 字节组成的字符串。所以我无法回答比这更长的字符串的问题,但是当您尝试通过以下方式将其转换为字节时,此字符串会导致错误:

byte[] blah = foo.getBytes();

出现错误:

Exception in thread "main" java.lang.NegativeArraySizeException: -1073741830

如果你能以某种方式想出一个以字节为单位更长的字符串,我希望你的表现不会更好。我希望这能回答您的“会发生什么”和“您将如何测试”的问题。

这是我的完整测试和输出:

public class Test {
    public static void main(String[] args) {

        // Display MAX_VALUE
        System.out.println(Integer.MAX_VALUE);

        // By a bit of trial and error,build the longest two-byte character string possible with String.repeat()
        String foo = "\uD83D".repeat((Integer.MAX_VALUE)/2-1);

        // Display the number of bytes this string takes to store,which is just short of Integer.MAX_VALUE
        System.out.println(foo.length());
        System.out.println(foo.length()*2);

        // This line craps out even though the String length in bytes is less than Integer.MAX_VALUE
        byte[] blah = foo.getBytes();
    }
}

结果:

2147483647
1073741822
2147483644
Exception in thread "main" java.lang.NegativeArraySizeException: -1073741830
    at java.base/java.lang.StringCoding.encodeUTF8_UTF16(StringCoding.java:910)
    at java.base/java.lang.StringCoding.encodeUTF8(StringCoding.java:885)
    at java.base/java.lang.StringCoding.encode(StringCoding.java:489)
    at java.base/java.lang.String.getBytes(String.java:981)
    at Test.main(Test.java:15)

您应该能够捕获在字符串处理过程中可能遇到的任何异常,这可能是在构建字符串时而不是将其转换为字节时遇到的。请记住捕获 Throwable,因为您将得到的大多数错误将是 RuntimeExceptions 而不是 ExceptionsThrowable 会抓住任何一个。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。