持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第3天,点击查看活动详情
前言
兄弟们,国庆节第一天大家过的还好吗?我早上6点起床,收拾行李,去车站坐车,回家吃午饭;然后睡了个午觉下午就差不多了哈哈哈哈,本来打算明天出去爬个山,但是一个人多少有一丝无聊吧哈哈。但是咱立的flag不能倒,今天记录一下按行读取数据量很大的文件的第二种方式:NIO;
NIO
简介
NIO 与原来的 IO 有同样的作用和目的,但是使用方式完全不同,NIO 支持面向缓冲区的、基于通道的 IO 操作。NIO 将以更加高效的方式进行文件的读写操作;NIO基于Channel和Buffer(缓冲区)进行操作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中;
与IO区别
IO:面向流,是阻塞的Io; NIo:面向缓冲区,是非阻塞Io;
通道和缓冲区
NIO的核心在于通道和缓冲区(及Selector(选择器)本次暂时没有用到),通道负责传输,缓冲区负责存储;通道用于源节点与目标节点的连接。Java NIO 中负责缓冲区中数据的传输。Channel 本身不存储数据,因此需要配合缓冲区进行传输;
缓冲区
Buffer(缓冲区)是一个读取数据的内存块,可以理解成一个容器对象(含数组),该对象提供了一组方法,可以轻松的使用内存块。缓冲区对象内置了一些机制,能够跟踪和记录缓冲区的状态变化情况。channel提供从文件、网络读取数据的渠道,但是读取和写入数据必须都经由buffer。
使用Buffer读写数据一般遵循以下四个步骤:
- 写入数据到Buffer
- 调用flip()方法,转换为读取模式
- 从Buffer中读取数据
- 调用buffer.clear()方法或者buffer.compact()方 法清除缓冲区
通道
NIO的通道类似于流,但有些区别:
区别1:通道可以同时进行读写,但是流只能读或者写
区别2:通道可以实现异步读写数据
区别3:通道可以从缓冲区读数据,也可以写数据到缓冲区
代码
/**
* 按行读取文件
* @param inCh 读通道
* @param rbuf 读缓冲区
* @param encode 编码
*/
public static void readByLine(FileChannel inCh,ByteBuffer rbuf,String encode){
try {
byte[] temp = new byte[0];//用来缓存上次读取剩下的部分
int LF = "\n".getBytes()[0];//换行符
while(inCh.read(rbuf) != -1){
int position = rbuf.position();
byte[] rbyte = new byte[position];
rbuf.flip();
rbuf.get(rbyte);
//判断是否有换行符
int startnum = 0;
for(int i = 0; i < rbyte.length; i++){
if(rbyte[i] == LF){//若存在换行符
byte[] line = new byte[temp.length + i - startnum + 1];
System.arraycopy(temp, 0, line, 0, temp.length);
System.arraycopy(rbyte, startnum, line, temp.length, i - startnum + 1);
startnum = i + 1;
temp = new byte[0];
//处理每一行数据
//DataParser dataParser = new DataParser(new String(line, StandardCharsets.ISO_8859_1).substring(0,14),new String(line, StandardCharsets.ISO_8859_1).substring(15,line.length-1));
//dataParser.run();
}
}
if(startnum < rbyte.length){//说明rbyte最后还剩不完整的一行
byte[] temp2 = new byte[temp.length + rbyte.length - startnum];
System.arraycopy(temp, 0, temp2, 0, temp.length);
System.arraycopy(rbyte, startnum, temp2, temp.length, rbyte.length - startnum);
temp = temp2;
}
rbuf.clear();
}
//兼容最后一行没有换行的情况
// if(temp.length > 0){
// DataParser dataParser = new DataParser(new String(temp, StandardCharsets.ISO_8859_1).substring(0,14),new String(temp, StandardCharsets.ISO_8859_1).substring(15));
// dataParser.run();
// }
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
inCh.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
try {
RandomAccessFile inraf = new RandomAccessFile("D:\testUnZip\2017\01\01\LDPGABAC9HE102082","r");
FileChannel inch = inraf.getChannel();
ByteBuffer rbuf = ByteBuffer.allocate(1024*4);
readByLine(inch,rbuf,"UTF_8");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
总结
最终领导选择让我用这种方式按行读取文件进行解析操作,但是我在自己测试的时候发现越往后用时越久,可能和后续的操作也有点儿关系吧,单独这一段代码的性能并没有特别准确的进行过测试;现在只是单纯的做个记录,有机会再深究吧!