造成NIO乱码的问题是因为读取的信息是按照特定编码读取的字节流信息,读取的时候受到读取限定,就有可能出现读取的不是一个完整的字节数组信息。
例如: 你好啊 。
在UTF-8的字节数组是[-28,-67,-96,-27,-91,-67,-27,-107,-118]。
在GBK的字节数组是[-60,-29,-70,-61,-80,-95]。
在unicode中数值是 20320,22909,21834。
转码就需要完整的字节信息,utf-8中[-28,-67,-96]代表 你 ,如果读取字节流信息的时候制度去到[-28]或者[-28,-67]这种不完整的编码,那么转码成unicode就会变成乱码。因此解决乱码就是需要我们将多读取的字节信息截留,下次完整读取。
以下是针对utf-8 与GBK文件的解决代码,其他编码没有测试过。
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
public class Test {
public static void main(String[] args) {
//GBK文件 使用 GBK编码
new Test().readFile("C:\\Users\\Administrator\\Desktop\\1.txt", "UTF-8");
}
public void readFile(String path, String charsetName) {
Charset c = Charset.forName(charsetName);
// 解码器,字节流信息转换为字符信息
CharsetDecoder decoder = c.newDecoder();
try (RandomAccessFile raf = new RandomAccessFile(path, "r"); FileChannel fc = raf.getChannel();) {
// 设置成5是因为不管是UTF-8还是GBK在全中文文档都会出字节信息被截取的情况,也就是所谓会出现乱码。
//这样方便测试
ByteBuffer bytes = ByteBuffer.allocate(5);
CharBuffer cb = CharBuffer.allocate(5);
int size = fc.read(bytes);
while (size != -1) {
bytes.flip();
//size<5,小于5的时候说明文件已经读取结束了
//为了防止源文件就是一个破损的文件,末尾有不符合规范的字节流
//当末尾的时候就不做截留操作,直接转码
decoder.decode(bytes, cb, size<5);
cb.flip();
System.out.print(cb.toString());
//全中文文档第一次读取的时候,五个字节就是说UTF-8和GBK的文档都会有一个字节信息未被读取
//这个时候需要保存多读取的信息,UTF-8中sub=2,后面两个属于非正常字节信息
//GBK的时候sub=1 只有一个非正常的自己信息
//后面继续读取的时候将会根据实际情况进行截留
int sub = bytes.limit()-bytes.position();
bytes.clear();
cb.clear();
//将读取的定位值 减去多读取的信息
fc.position(fc.position()-sub);
size = fc.read(bytes);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}