解决拆包问题
1、按行读取文件;
2、拆包、粘包问题;
3、引入 ByteArrayOutputStream 可变字节数组;
4、需要了解ByteBuffer中的position、limit的意义;
需要适配兼容如下场景
1、ByteBuffer 可长可短(1-2048);
2、ByteBuffer中的最后一二个字节可能是一个中文字符的部分字节(一个汉字三个字节);
3、ByteBuffer中的前面一二个字节可能是上次读取最后一个字符的部分字节;
4、某一行字符特别长,ByteBuffer可能不存在换行符号(\n);
5、行字符数特别少,ByteBuffer中可能存中多个换行符号;
目标文件 test.txt
有中文,行有长有短;
开始
自从
黑化黑灰化肥黑化黑灰化肥黑化黑灰化肥黑化黑灰化肥黑化黑灰化肥黑化黑灰化肥黑化
my name is 化肥黑化黑灰化肥黑化黑灰化肥黑化黑灰化肥黑化黑灰化肥黑化
黑
化
肥
料
a
b
cccccc
dd
无所事事
结束
案例代码如下
方案1:
public static void main2() {
String filePath = "F:\\workspace\\_java\\netty-basic\\src\\main\\java\\com\\io\\basic\\nio\\test.txt";
// 1. 创建 RandomAccessFile 对象
try (FileChannel channel = new RandomAccessFile(filePath, "r").getChannel()) {
// byteBuffer 可长可短
ByteBuffer byteBuffer = ByteBuffer.allocate(2);
Charset charset = StandardCharsets.UTF_8;
ByteBuffer beforeBuffer = null;
while (channel.read(byteBuffer) > 0) {
// 切换为读模式
byteBuffer.flip();
boolean hasGapCode = false;
for (int i = 0; i < byteBuffer.limit(); i++) {
//
char b = (char)byteBuffer.get(i);
if (b == '\n') {
// byteBuffer 存在换行符
hasGapCode = true;
int len = i + 1 - byteBuffer.position();
ByteBuffer newBuffer = null;
if (beforeBuffer != null) {
if (beforeBuffer.hasRemaining()) {
newBuffer = ByteBuffer.allocate(len + beforeBuffer.limit());
for (int j = 0; j < beforeBuffer.limit(); j++) {
newBuffer.put(beforeBuffer.get());
}
beforeBuffer = null;
}
}
if (newBuffer == null) {
newBuffer = ByteBuffer.allocate(len);
}
for (int j = 0; j < len; j++) {
newBuffer.put(byteBuffer.get());
}
newBuffer.flip();
System.out.print(charset.decode(newBuffer));
}
}
if (hasGapCode) {
byteBuffer.compact();
} else {
if(beforeBuffer == null || !beforeBuffer.hasRemaining()) {
beforeBuffer = ByteBuffer.allocate(byteBuffer.limit());
} else {
ByteBuffer temp = beforeBuffer;
beforeBuffer = ByteBuffer.allocate(byteBuffer.limit() + temp.limit());
for (int i = 0; i < temp.limit(); i++) {
beforeBuffer.put(temp.get());
}
}
for (int i = 0; i < byteBuffer.limit(); i++) {
beforeBuffer.put(byteBuffer.get());
}
beforeBuffer.flip();
byteBuffer.clear();
}
}
if(beforeBuffer!=null) {
if (beforeBuffer.hasRemaining()) {
System.out.println(charset.decode(beforeBuffer));
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
方案2:
package com.io.basic.nio;
import java.io.ByteArrayOutputStream;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
/**
* @author gy
* @date 2024-02-25
* 解决按行读取文件内容,粘包拆包的问题
*/
public class ByteBufferDemo {
public static void main(String[] args) {
String filePath = "F:\\workspace\\_java\\netty-basic\\src\\main\\java\\com\\io\\basic\\nio\\test.txt";
// 1. 创建 RandomAccessFile 对象
try (FileChannel channel = new RandomAccessFile(filePath, "r").getChannel()) {
// 容量是随意设置的
ByteBuffer byteBuffer = ByteBuffer.allocate(2);
Charset charset = StandardCharsets.UTF_8;
// 可变字节数组
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
while (channel.read(byteBuffer) > 0) {
// 切换为读模式
byteBuffer.flip();
while (byteBuffer.hasRemaining()) {
byte b = byteBuffer.get();
// 解决拆包(行)问题
byteArrayOutputStream.write(b);
if (b == '\n') {
// 按 换行符 进行拆分
System.out.print(new String(byteArrayOutputStream.toByteArray(), charset));
byteArrayOutputStream.reset();
}
}
// 切换写
byteBuffer.clear();
}
if (byteArrayOutputStream.size() > 0) {
System.out.println(new String(byteArrayOutputStream.toByteArray(), charset));
}
byteArrayOutputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}