JAVA NIO(三):缓冲区的相互转换及中文乱码的解决方案

在Java IO中,Channel(通道)只能直接与ByteBuffer进行通信,这样我们可能会用ByteBuffer的视图来解决数据的转换问题,如将字符串转换为二进制缓冲区,整数缓冲区转换为二进制缓冲区,示例如下:

ByteBuffer buffer = ByteBuffer.allocate(100);
//  获取缓冲区的视图,但与ByteBuffer的mark、position、limit互相独立
CharBuffer charBuff = buffer.asCharBuffer();
//  更容易进行字符操作
charBuff.put("Hello, World!");
//  创建输出通道
WritableByteChannel outChannel = Channels.newChannel(System.out);
//  写入缓冲区数据
outChannel.write(byteBuf);
outChannel.close();

采用上述的方法,会导致以下几个问题:
1. 难以控制字符与字节的转换过程;
2. 难以获取缓冲区的当前位置与剩余空间;
3. 难以控制缓冲区的阶段读取,如20-50,50-70;

在上面的例子中,我们将CharBuffer与ByteBuffer进行了转换,极大地简化了字符的操作行为,但可惜的是,字符依赖于系统编码(这里是UTF-8,每个字符占两个字节),写入的时候每个字符占两个字节,但读取的时候却不知道编码,最后导致出现乱码。

要解决上述的问题,只有自己控制字符与字节转换的过程,才能保证字符的输入与输出保持一致,如下:

//  创建输出通道
WritableByteChannel outChannel = Channels.newChannel(System.out);
String text = "你好,JAVA!";
byte[] arr = text.getBytes("UTF-8");
ByteBuffer byteBuf = ByteBuffer.wrap(arr);
outChannel.write(byteBuf);
outChannel.close();

使用上述的方法,优点是能保证字符的正确转换,但是很显然,效率太低了,还有更好的办法吗?

经过实测,通过字节转换的效率并不低,并且比CharBuffer放入字符串的速度高4-5倍,对比数据如下:

字节数量ByteBuffer直接放入数据通过CharBuffer放入数据
1200PT0.000087808SPT0.000437138S
2400PT0.000099591SPT0.000669771S
4800PT0.000140644SPT0.000880357S

这里最有意思的地方在于,CharBuffer统一以两个字节存储一个字符,而ByteBuffer则依赖于编码,可能是两个字节,也可能是三个字节,最后导致512个字符,CharBuffer的limit为512,换算为ByteBuffer后为1024个字节,而直接用ByteBuffer存储字节则为1200(依赖于具体的字符,不定),如下:

字符数量ByteBuffer直接放入数据通过CharBuffer放入数据
5121200512
102424001024
204848001024

关于乱码,如果是以CharBuffer放入的数据,因为没有对应的字符集解码器,必定是乱码,只有通过同样的CharBuufer方式读出,才能正确解决,所以在这里,char是一种数据存储格式,就如同long数据、int数据一样,并不是操作系统上对应的字符集,所以无法解析。尤其是文本编辑器提示“此文档包含当前文本编码无法处理的字符”,则可以断定是以字符数据格式进行存储,处理方式如下:

FileInputStream fis = new FileInputStream(file);
FileChannel fc = fis.getChannel();
ByteBuffer bbuf = ByteBuffer.allocateDirect(1024);
long offset = 0;
long nums = 0;
while((nums = fc.read(bbuf, offset)) > 0) {
    //  一定要反转回到正确位置,CharBuffer视图是基于当前位置创建的
    bbuf.flip();
    //  以字符数据方式进行读取
    CharBuffer cbuf = bbuf.asCharBuffer();
    bbuf.clear();
    offset = offset + nums;
}
fc.close();
fis.close();

结论

  1. 一定要区分字符与字符数据,如果是操作系统上的字符,则可对应到文本编码,并有相应的字符集编码,而字符数据必定是二进制编码;
  2. 缓冲区存储的是字节(不是字符);
  3. 字符由多个字节组成,如果不能按照指定的顺序与数量读取所需的字节,必然出现乱码。
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值