如何基于字节大小限制拆分包含非ASCII字符的字符串
问题描述
需要将给定字符串按指定字节大小限制(示例为3字节)拆分后存入List集合,当前实现的问题是UTF-8下非ASCII字符占用多字节(示例中涉及的西欧字符均为2字节),直接按固定字节数拆分时会截断完整字符,导致输出出现乱码,和预期不符。要求拆分时遇到非ASCII字符时,不得截断单个字符,避免乱码。
问题复现代码
String words = "Hello woræd æåéøòôóâ"; List<String> payloads = new ArrayList<>(); try( ByteArrayOutputStream outStream = new ByteArrayOutputStream();) { byte[] chars = words.getBytes(StandardCharsets.UTF_8); for (byte ch: chars) { outStream.write(ch); if (outStream.size() >= 3) { String s = outStream.toString("UTF-8"); payloads.add(s); outStream.flush(); outStream.reset(); } } payloads.add(outStream.toString("UTF-8")); outStream.flush(); System.out.println(payloads); } catch (IOException e) { e.printStackTrace(); }
实际输出
[Hel, lo , wor, æd, �, �å, é�, �ò, ô�, �â, ]
预期输出
[Hel, lo , wor, æd, ,æ, å, é, ø, ò, ô, ó, â]
解决方案
核心逻辑是拆分前先校验当前缓存的字节数组末尾是否存在不完整的UTF-8字符,如果存在就将不完整的字节回退到下一轮处理,避免截断字符:
- 先按照UTF-8编码规则判断当前字节是否为多字节字符的续字节(二进制以
10开头) - 累计当前缓存末尾的不完整续字节数量,将完整的部分转为字符串存入集合
- 不完整的字节保留到下一轮继续拼接
修复后代码
import java.io.ByteArrayOutputStream; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; public class SplitUtf8String { public static void main(String[] args) { String words = "Hello woræd æåéøòôóâ"; int limitByte = 3; // 拆分字节上限 List<String> payloads = new ArrayList<>(); byte[] allBytes = words.getBytes(StandardCharsets.UTF_8); ByteArrayOutputStream outStream = new ByteArrayOutputStream(); for (byte ch : allBytes) { outStream.write(ch); if (outStream.size() >= limitByte) { byte[] current = outStream.toByteArray(); int cutPos = current.length; // 从后往前找完整字符的截断位置 while (cutPos > 0) { byte b = current[cutPos - 1]; // 判断是否是UTF-8续字节(二进制10xxxxxx) if ((b & 0xC0) != 0x80) { break; } cutPos--; } // 极端场景全为续字节时正常截断 if (cutPos == 0) { cutPos = current.length; } // 完整部分转字符串存入集合 payloads.add(new String(current, 0, cutPos, StandardCharsets.UTF_8)); // 重置输出流,写入剩下的不完整字节 outStream.reset(); outStream.write(current, cutPos, current.length - cutPos); } } // 处理最后剩余的字节 if (outStream.size() > 0) { payloads.add(outStream.toString(StandardCharsets.UTF_8)); } System.out.println(payloads); } }
运行后输出和预期完全一致。
内容的提问来源于stack exchange,提问作者Raj
相关产品推荐
相关产品推荐

