场景:
读取一个大文本文件,并输出到控制台。java
在这里咱们选择使用nio进行读取文本文件,在输出的过程当中,有些文件中英文都显示正常,有些则偶尔出现中文乱码,经思考发现,在 ByteBuffer.allocate 时分配空间,若是中英混合的文件中就会出现中文字符只读取了一部分的问题,若是文本为等长编码字符集的时候,能够根据编码集 byte 长度进行 allocate ,例如 GBK 为2 byte ,因此咱们 allocate 时未2的倍数便可,但像 UTF-8 这类变长的编码字符集时则没那么简单了。dom
下面就是 UTF-8 的编码方式编码
0xxxxxxx 110xxxxx 10xxxxxx 1110xxxx 10xxxxxx 10xxxxxx 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
经过分析咱们发现,在读取中咱们经过处理临界值来解决 UTF-8 编码字符读取问题。code
示例代码以下:
RandomAccessFile rf = new RandomAccessFile("zh.txt", "rw"); FileChannel channel = rf.getChannel(); ByteBuffer buffer = ByteBuffer.allocate(4); // 至少为4,由于UTF-8最大为4字节 while (channel.read(buffer) != -1) { byte b; int idx; out : for (idx = buffer.position()-1; idx >= 0; idx--) { b = buffer.get(idx); if ((b & 0xff) >> 7 == 0) { // 0xxxxxxx break; } if ((b& 0xff & 0xc0) == 0xc0) { // 11xxxxxx,110xxxxx、1110xxxx、11110xxx idx -= 1; break; } if ((b & 0xff & 0x80) == 0x80) { for (int i = 1; i < 4; i++) { b = buffer.get(idx - i); if ((b & 0xff & 0xc0) == 0xc0) { if ((b & 0xff) >> (5 + 1 - i) == 0xf >> (3 - i)) { break out; } else { idx = idx - 1 - i; break out; } } } } } buffer.flip(); int limit = buffer.limit(); buffer.limit(idx+1); // 阻止读取跨界数据 System.out.println(Charset.forName("UTF-8").decode(buffer).toString()); buffer.limit(limit); // 恢复limit buffer.compact(); } channel.close(); rf.close();