如何用Java将字符串剪切成1兆字节的子字符串?

时间:2017-04-19 15:25:32

标签: java

我想出了以下内容:

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;
    }
}

但我的问题是,使用UTF-8某些字符并不完全占用1个字节,因此使用1048576告诉切割字符串的位置不起作用。我在考虑使用Iterator,但这看起来并不高效。什么是最有效的解决方案?字符串可以小于1 Mb以避免字符切片,只是不大于那个!

2 个答案:

答案 0 :(得分:4)

快速,不安全的黑客

您可以使用System.out.println("¡Adiós!".getBytes("UTF-8").length); // Prints: 9 获取一个数组,其中包含每个UTF-8字符使用的实际字节数。像这样:

new String(chunk, "UTF-8")

完成后,只需将字节数组拆分为长度为1048576的块,然后将块转换为带有getBytes的UTF-8字符串。

然而,通过这样做,您可以在块的开头或结尾处打破多字节字符。假设1048576th字符是一个3字节的Unicode字符:第一个字节将进入第一个块,另外两个字节将被放入第二个块,从而破坏编码。

正确的方法

如果你可以放宽“1 MB”的要求,你可以采取一种更安全的方法:将字符串拆分成1048576个字符(不是字节)的块,然后用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; } 测试每个块的实际长度,删除字符根据需要从实际大小等于或小于1 MB。

这是一个不会破坏字符的实现,代价是让某些行小于给定的大小:

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 */ 测试了它,所以你可以看到效果。它应该适用于任何其他大小:

    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
     */

使用您在评论中提到的3 MB字符串进行的另一项测试:

{{1}}

答案 1 :(得分:1)

您可以将ByteArrayOutputStream与OutputStreamWriter

一起使用
Private Sub DataGridView1_CellClick(sender As Object, e As DataGridViewCellEventArgs) Handles DataGridView1.CellClick
        'does not work
        DataGridView1.Rows(e.RowIndex).Cells(6).Visible = True         
End Sub