学习NIO过程中老师门都会讲一个ByteBuffer读写文件的例子,可是我把文件内容换成中文就不OK了
1.源码
public class FileChannelDemo1 {
// FileChannel读取数据到buffer中
public static void main(String[] args) throws Exception {
// 创建FileChannel
RandomAccessFile aFile = new RandomAccessFile("d:/中文文本.txt", "rw");
FileChannel channel = aFile.getChannel();
// 创建Buffer
ByteBuffer byteBuffer = ByteBuffer.allocate(21);
// 读取数据到buffer中
int bytesRead = channel.read(byteBuffer);
while (bytesRead != -1) {
// System.out.print("读取了:" + bytesRead);
byteBuffer.flip();
while (byteBuffer.hasRemaining()) {
System.out.print(StandardCharsets.UTF_8.decode(byteBuffer));
}
byteBuffer.compact();
bytesRead = channel.read(byteBuffer);
}
aFile.close();
}
}
/*
d:/中文文本.txt
中华人民共和国中华人民共和国中华人民共和国中华人民共和国中华人民共和国中华人民共和国中华人民共和国中华人民共和国
中华人民共和国中华人民共和国中华人民共和国中华人民共和国中华人民共和国中华人民共和国中华人民共和国中华人民共和国
中华人民共和国中华人民共和国中华人民共和国中华人民共和国中华人民共和国中华人民共和国中华人民共和国中华人民共和国
中华人民共和国中华人民共和国中华人民共和国中华人民共和国中华人民共和国中华人民共和国中华人民共和国中华人民共和国
中华人民共和国中华人民共和国中华人民共和国中华人民共和国中华人民共和国中华人民共和国中华人民共和国中华人民共和国
中华人民共和国中华人民共和国中华人民共和国中华人民共和国中华人民共和国中华人民共和国中华人民共和国中华人民共和国
*/
2.输出 image.png
问题很清楚:中文是由多字节组成,而ByteBuffer按字节处理数据,余下部分不能组成完整的字符,要和下一组数据合并处理才能组成完整字符。
于是百度之
https://blog.csdn.net/hello_junz/article/details/87706732
public class FileChannelTest {
public static void main(String[] args) throws IOException {
FileChannelTest fcChannelTest = new FileChannelTest();
fcChannelTest.readFile();
}
public void readFile() throws IOException {
// 文件编码是utf8,需要用utf8解码
Charset charset = Charset.forName("utf-8");
CharsetDecoder decoder = charset.newDecoder();
File file = new File(getClass().getResource("").getPath(), "中文测试.txt");
RandomAccessFile raFile = new RandomAccessFile(file, "rw");
FileChannel fChannel = raFile.getChannel();
ByteBuffer bBuf = ByteBuffer.allocate(32); // 缓存大小设置为32个字节。仅仅是测试用。
CharBuffer cBuf = CharBuffer.allocate(32);
int bytesRead = fChannel.read(bBuf); // 从文件通道读取字节到buffer.
char[] tmp = null; // 临时存放转码后的字符
byte[] remainByte = null;// 存放decode操作后未处理完的字节。decode仅仅转码尽可能多的字节,此次转码不了的字节需要缓存,下次再转
int leftNum = 0; // 未转码的字节数
while (bytesRead != -1) {
bBuf.flip(); // 切换buffer从写模式到读模式
decoder.decode(bBuf, cBuf, true); // 以utf8编码转换ByteBuffer到CharBuffer
cBuf.flip(); // 切换buffer从写模式到读模式
remainByte = null;
leftNum = bBuf.limit() - bBuf.position();
if (leftNum > 0) { // 记录未转换完的字节
remainByte = new byte[leftNum];
bBuf.get(remainByte, 0, leftNum);
}
// 输出已转换的字符
tmp = new char[cBuf.length()];
while (cBuf.hasRemaining()) {
cBuf.get(tmp);
System.out.print(new String(tmp));
}
bBuf.clear(); // 切换buffer从读模式到写模式
cBuf.clear(); // 切换buffer从读模式到写模式
if (remainByte != null) {
bBuf.put(remainByte); // 将未转换完的字节写入bBuf,与下次读取的byte一起转换
}
bytesRead = fChannel.read(bBuf);
}
raFile.close();
}
}
3 改造
以上程序逻辑是正确的,能够输出正确结果,但他是使用硬编码的方式处理剩余字节的,其实ByteBuffer 提供这样的api来处理这种问题,就是compact(),本着大道至简的原则对程序加以改造,还请各位看官多提宝贵意见。
public class FileChannelTest {
public static void main(String[] args) throws IOException {
RandomAccessFile raFile = new RandomAccessFile("d:/中文文本.txt", "rw");
FileChannel fChannel = raFile.getChannel();
ByteBuffer byteBuffer = ByteBuffer.allocate(32);
CharBuffer charBuffer = CharBuffer.allocate(32);
CharsetDecoder decoder = Charset.defaultCharset().newDecoder();
while (fChannel.read(byteBuffer) != -1) {
byteBuffer.flip();
decoder.decode(byteBuffer, charBuffer, true);
charBuffer.flip();
while (charBuffer.hasRemaining()) {
System.out.print(charBuffer.get());
}
byteBuffer.compact();// 压缩:把未读过的内容保留,已读过的内容清理掉
charBuffer.clear();
}
raFile.close();
}
}
image.png
网友评论