问题描述
给定一个长度为 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
而不是 Exceptions
。 Throwable
会抓住任何一个。