使用Java NIO FileChannel 读取文件时UTF-8中文乱码问题解决
在上次笔记中记录了FileChannel相关的知识,准备通过java swing做一个Windows的记事本还原,目前还在开发当中,目前界面如下:
后续还会对界面和功能进行完善和开发。目前在读取文件时遇到一点小问题,我这里想默认暂时用UTF-8去打开文件,这时候由于UTF-8是不定长编码,而且通过FileChannel读取文件的话我们得首先分配ByteBuffer的大小,这种情况下如果读取到某个汉字时它需要的字节大小小于分配大小剩余时就会出现乱码的问题。
解决方法
通过查阅网上的资料时发现下面这个方法:
https://blog.csdn.net/hello_junz/article/details/87706732
我在他的基础上做了一点改动,整体思路是一致的。解决方法是除了使用ByteBuffer这个缓冲区外我们再创建一个字符缓冲区,CharBuffer,用它来存储我们转码后的字符,转码用到了CharsetDecoder这个类的decod()方法来转码,它会将ByteBuffer中的字节尽可能的转码,没有转码的会留在ByteBuffer,我们可以对CharBuffer进行处理,例如把他拼接到StringBuilder中。重点在这里,上面博文中是使用一个变量和一个数组来判断和存储未被转码的字节,然后切换为写入模式后再讲未转码的字节数组put到原ByteBuffer的开头。我这里是在切换为写入模式时使用compact()方法代替clear()方法,compact()方法只清空已读取的数据,未读取的数据会被留到下次读取,具体可看Java NIO笔记02中的介绍。
代码如下:
public String readFile(String filePath) {
StringBuilder sb = new StringBuilder();
//使用UTF-8编码
Charset charset = Charset.forName(StandardCharsets.UTF_8.name());
CharsetDecoder decoder = charset.newDecoder();
char[] charCache = null;
try (RandomAccessFile file = new RandomAccessFile(filePath, "rw");
FileChannel fileChannel = file.getChannel();) {
ByteBuffer bBuf = ByteBuffer.allocate(1024);
CharBuffer cBuf = CharBuffer.allocate(1024);
while (fileChannel.read(bBuf) != -1) {
bBuf.flip();
//从给定的输入缓冲区解码尽可能多的字节,将结果写入到给定的缓冲区
decoder.decode(bBuf, cBuf, true);
cBuf.flip();
charCache = new char[cBuf.length()];
//处理解码成功的字符集
while (cBuf.hasRemaining()) {
cBuf.get(charCache);
sb.append(charCache);
}
//转为写入模式
bBuf.compact(); //注意这里是适用compact清空缓存,它和clear的区别是不会整个清空,而是清空已经读取了的数据,未读取的数据会继续被设置到缓存区的开头位置下次读取
cBuf.clear();
}
} catch (IOException e) {
e.printStackTrace();
}
return sb.toString();
}