Saya memiliki potongan kode ini yang dimaksudkan untuk membagi string menjadi array string menggunakan CHUNK_SIZE sebagai ukuran pemisahan, dalam byte (saya melakukan ini untuk hasil paginasi). Ini berfungsi dalam banyak kasus ketika karakter adalah 1 byte, tetapi ketika saya memiliki karakter multi-byte (seperti misalnya karakter Prancis 2-byte (seperti é) atau karakter Cina 4 byte) di lokasi yang tepat, saya berakhir dengan 2 karakter yang tidak dapat dibaca di akhir elemen array pertama saya dan di awal yang kedua.

Apakah ada cara untuk memperbaiki kode untuk memperhitungkan karakter multibyte sehingga dipertahankan dalam hasil akhir?

public static ArrayList<String> splitFile(String data) throws Exception {
    ArrayList<String> messages = new ArrayList<>();
    int CHUNK_SIZE = 400000;// 0.75mb

    if (data.getBytes().length > CHUNK_SIZE) {
        byte[] buffer = new byte[CHUNK_SIZE];
        int start = 0, end = buffer.length;
        long remaining = data.getBytes().length;
        ByteArrayInputStream inputStream =
                new ByteArrayInputStream(data.getBytes());

        while ((inputStream.read(buffer, start, end)) != -1) {
            ByteArrayOutputStream outputStream =
                    new ByteArrayOutputStream();
            outputStream.write(buffer, start, end);
            messages.add(outputStream.toString("UTF-8"));
            remaining = remaining - end;

            if (remaining <= end) {
                end = (int) remaining;
            }
        }
        return messages;
    }

    messages.add(data);
    return messages;
}
0
stfudonny 29 Desember 2020, 05:14

2 jawaban

Jawaban Terbaik
public static List<String> splitFile(String data) throws IOException {
    List<String> messages = new ArrayList<>();
    final int CHUNK_SIZE = 400_000;// 0.75mb

    byte[] dataBytes = data.getBytes(StandardCharsets.UTF_8);
    byte[] buffer = new byte[CHUNK_SIZE];
    int start = 0;
    final int end = CHUNK_SIZE;
    ByteArrayInputStream inputStream = new ByteArrayInputStream(dataBytes);

    for (; ; ) {
        int read = inputStream.read(buffer, start, end - start);
        if (read == -1) {
            if (start != 0) {
                messages.add(new String(buffer, 0, start,
                        StandardCharsets.UTF_8));
            }
            break;
        }
        // Check for half read multi-byte sequences:
        int fullEnd = start + read;
        while (fullEnd > 0) {
            byte b = buffer[fullEnd - 1];
            if (b >= 0) { // ASCII.
                break;
            }
            if ((b & 0xC0) == 0xC0) { // Start byte of sequence.
                --fullEnd;
                break;
            }
            --fullEnd;
        }
        messages.add(new String(buffer, 0, fullEnd, StandardCharsets.UTF_8));
        start += read - fullEnd;
        if (start > 0) { // Copy the bytes after fullEnd to the start.
            System.arraycopy(buffer, fullEnd, buffer, 0, start);
            //               src     srcI     dest    destI len
        }
    }
    return messages;
}

Saya telah menyimpan ByteArrayInputStream, seperti yang paling sering dibaca dari InputStream, alih-alih memiliki semua byte di memori.

Kemudian buffer chunk dibaca, dari start daripada dari 0, karena mungkin ada beberapa byte yang tertinggal dari chunk sebelumnya yang dibaca.

Membaca memberikan jumlah byte yang dibaca atau -1.

Pada akhirnya, karakter ASCII tidak apa-apa, jika tidak, saya memposisikan akhir di awal urutan multibyte. Mungkin urutan itu benar-benar dibaca, mungkin tidak. Di sini saya hanya menyimpannya untuk potongan berikutnya yang sedang dibaca.

Kode ini tidak melihat kompiler.

Daftar pesan juga tidak ramah memori.

BTW pada char[] seseorang akan memiliki masalah yang sama, terkadang titik kode Unicode, simbol, adalah dua (UTF-16) karakter.

1
Joop Eggen 7 Januari 2021, 05:22

Karena Anda melakukan ini untuk membuat halaman hasil, mungkin berguna untuk membagi teks ini bukan dengan karakter tetapi dengan kata-kata. Anda dapat mengulangi indeks karakter string ini dan memeriksa setiap kata apakah setidaknya setengahnya cocok dengan halaman, dan jika tidak, memulai halaman baru.

Contoh dengan ukuran garis terbatas pada satu halaman. Ini berfungsi sama dengan ukuran halaman terbatas dalam dokumen multi-halaman:

String text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, " +
        "sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. " +
        "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris " +
        "nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in " +
        "reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla " +
        "pariatur. Excepteur sint occaecat cupidatat non proident, sunt in " +
        "culpa qui officia deserunt mollit anim id est laborum.";

int length = 55;

ArrayList<String> lines = new ArrayList<>();

int lastWord = 0;
int lastLine = 0;
for (int i = 0; i < text.length(); i++) {
    if (text.charAt(i) == ' ') {
        if (i - lastLine + (i - lastWord) / 2 > length) {
            lines.add(text.substring(lastLine, i));
            lastLine = i + 1;
        }
        lastWord = i + 1;
    }
}
lines.add(text.substring(lastLine));

// output line by line
lines.forEach(System.out::println);

Keluaran:

Lorem ipsum dolor sit amet, consectetur adipiscing elit,
sed do eiusmod tempor incididunt ut labore et dolore magna
aliqua. Ut enim ad minim veniam, quis nostrud exercitation
ullamco laboris nisi ut aliquip ex ea commodo consequat.
Duis aute irure dolor in reprehenderit in voluptate velit
esse cillum dolore eu fugiat nulla pariatur. Excepteur
sint occaecat cupidatat non proident, sunt in culpa qui
officia deserunt mollit anim id est laborum.

Lihat juga: Bagaimana cara membagi string setelah panjang tertentu? Tapi itu harus dibagi setelah kata selesai

1
31 Desember 2020, 08:39