目标:分批次将UTF8字节转换中文字符
关键问题:
1. UTF8中汉字占的字节数3-4个,因此需要注意分批时汉字被切割。
2. “StandardCharsets.UTF_8”未成功解析的字节不会写入新的缓冲区中。
3. 需要记录每次读取的位置。
4. Arrays.copyOfRange会的to位置过大有可能会造成空读,超出部分它照样会创建空间。
思路:
- 分批次获取到字节的一部分,每次获取尽量不要小于5是最好的(最低也要3)。
- 利用字节缓冲区的position指针与limit指针计算出未成功读出的字节数,计入到下一次的读取中。
代码:
@Test
public void for_test_chinese() throws CharacterCodingException {
var raw = "长坂桥头a杀气生,横枪立马b眼圆睁。一声好似c轰雷震,独退曹家d百万兵。";
// 转转字节
var rawBytes = StandardCharsets.UTF_8.encode(raw).array();
// 总字节数
int maxLength = rawBytes.length;
// 每次读取字节数,值不能小于5,太小会导致一个字都读不出来
int intervalSize = 6;
// 缓冲区大小,不能小于每次读取字节数
int bufferSize = 12;
// 初始读取字节位置
int nowLength = 0;
while (maxLength > nowLength){
int value = maxLength - nowLength;
// 判断是否超出数据
if (value < intervalSize){
intervalSize = value;
}
// 分批解码UTF字节到字符串
var rawBatchBytes = Arrays.copyOfRange(rawBytes, nowLength, intervalSize + nowLength);
// 字节缓冲区
var byteBuffer = ByteBuffer.allocate(bufferSize);
// 将已提取的字节写入
byteBuffer.put(rawBatchBytes);
// 翻转进入读模式
byteBuffer.flip();
// 字符缓冲区
var charBuffer = CharBuffer.allocate(bufferSize);
// 转换字符,写入charBuffer。注意byteBuffer中无法解释的部分不会写入charBuffer,因此多出的字节不能计算
StandardCharsets.UTF_8.newDecoder().decode(byteBuffer, charBuffer,true);
// 翻转进入读模式
charBuffer.flip();
// 创建字符集合
var tmp = new char[charBuffer.length()];
// 判断charBuffer是否有数据
if (charBuffer.hasRemaining()){
// 写入字符集合
charBuffer.get(tmp);
// 打印数据
System.out.println(tmp);
}
// 计算下次从那里开始读,未写入部分不计入。
nowLength = nowLength + intervalSize - (byteBuffer.limit() - byteBuffer.position());
}
}