我想出了以下几点:
public static void cutString(String s) {
List<String> strings = new ArrayList<>();
int index = 0;
while (index < s.length()) {
strings.add(s.substring(index, Math.min(index + 1048576, s.length())));
index += 1048576;
}
}
Run Code Online (Sandbox Code Playgroud)
但我的问题是,使用 UTF-8 某些字符并不完全占用 1 个字节,因此使用 1048576 来告诉在哪里剪切字符串不起作用。我正在考虑使用迭代器,但这似乎效率不高。什么是最有效的解决方案?字符串可以小于 1 Mb 以避免字符切片,但不能大于 1 Mb!
快速、不安全的黑客攻击
您可以使用s.getBytes("UTF-8")获取一个包含每个 UTF-8 字符使用的实际字节的数组。像这样:
System.out.println("¡Adiós!".getBytes("UTF-8").length);
// Prints: 9
Run Code Online (Sandbox Code Playgroud)
一旦有了它,只需将字节数组拆分为长度为 1048576 的块,然后将这些块转换回 UTF-8 字符串new String(chunk, "UTF-8")。
但是,通过这样做,您可以在 chunks 的开头或结尾中断多字节字符。假设第 1048576 个字符是一个 3 字节的 Unicode 字符:第一个字节将进入第一个块,其他两个字节将放入第二个块,从而破坏编码。
如果您可以放宽“1 MB”的要求,您可以采取更安全的方法:将字符串拆分为 1048576 个字符(不是字节)的块,然后使用 测试每个块的实际长度getBytes,根据需要从末尾删除字符直到真正大小等于或小于 1 MB。
这是一个不会破坏字符的实现,代价是某些行小于给定大小:
public static List<String> cutString(String original, int chunkSize, String encoding) throws UnsupportedEncodingException {
List<String> strings = new ArrayList<>();
final int end = original.length();
int from = 0, to = 0;
do {
to = (to + chunkSize > end) ? end : to + chunkSize; // next chunk, watch out for small strings
String chunk = original.substring(from, to); // get chunk
while (chunk.getBytes(encoding).length > chunkSize) { // adjust chunk to proper byte size if necessary
chunk = original.substring(from, --to);
}
strings.add(chunk); // add chunk to collection
from = to; // next chunk
} while (to < end);
return strings;
}
Run Code Online (Sandbox Code Playgroud)
我用它测试过,chunkSize = 24所以你可以看到效果。它应该适用于任何其他尺寸:
String test = "En la fase de maquetación de un documento o una página web o para probar un tipo de letra es necesario visualizar el aspecto del diseño. ?(-???-?)? ?(????•?)? ?(?????)? ?(-???•?).";
for (String chunk : cutString(test, 24, "UTF-8")) {
System.out.println(String.format(
"Chunk [%s] - Chars: %d - Bytes: %d",
chunk, chunk.length(), chunk.getBytes("UTF-8").length));
}
/*
Prints:
Chunk [En la fase de maquetaci] - Chars: 23 - Bytes: 23
Chunk [ón de un documento o un] - Chars: 23 - Bytes: 24
Chunk [a página web o para pro] - Chars: 23 - Bytes: 24
Chunk [bar un tipo de letra es ] - Chars: 24 - Bytes: 24
Chunk [necesario visualizar el ] - Chars: 24 - Bytes: 24
Chunk [aspecto del diseño. ?(] - Chars: 22 - Bytes: 24
Chunk [-???-?)? ?(???] - Chars: 14 - Bytes: 24
Chunk [?•?)? ?(????] - Chars: 12 - Bytes: 23
Chunk [?)? ?(-???•?).] - Chars: 14 - Bytes: 24
*/
Run Code Online (Sandbox Code Playgroud)
另一个使用 3 MB 字符串的测试,就像您在评论中提到的那样:
String string = "0123456789ABCDEF";
StringBuilder bigAssString = new StringBuilder(1024*1024*3);
for (int i = 0; i < ((1024*1024*3)/16); i++) {
bigAssString.append(string);
}
System.out.println("bigAssString.length = " + bigAssString.toString().length());
bigAssString.replace((1024*1024*3)/4, ((1024*1024*3)/4)+1, "á");
for (String chunk : cutString(bigAssString.toString(), 1024*1024, "UTF-8")) {
System.out.println(String.format(
"Chunk [...] - Chars: %d - Bytes: %d",
chunk.length(), chunk.getBytes("UTF-8").length));
}
/*
Prints:
bigAssString.length = 3145728
Chunk [...] - Chars: 1048575 - Bytes: 1048576
Chunk [...] - Chars: 1048576 - Bytes: 1048576
Chunk [...] - Chars: 1048576 - Bytes: 1048576
Chunk [...] - Chars: 1 - Bytes: 1
*/
Run Code Online (Sandbox Code Playgroud)