/*************************************************************
*
* Copyright (c) 2015 yirendai.com, Inc. All Rights Reserved
*
*************************************************************/
/**
* @file BufferedFile.java
* @author melody(zechengzhao@yirendai.com)
* @date 2016年3月31日
* @brief 对RandomAccessFile添加缓存,提高存储性能。
*/
package com.yirendai.infra.cicada.util;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.LinkedList;
import java.util.List;
public final class BufferedFile {
private static final int BUFFER_LEN = 1 << 20;
private final RandomAccessFile file;
private long curOffset;
private byte[] buffer;
// 用于在buffer中定位一行的起止下标
private int startPos;
private int endPos;
private int readBytes;
private BufferedFile(final RandomAccessFile file) {
this.file = file;
this.curOffset = 0;
this.buffer = null;
this.startPos = 0;
this.endPos = 0;
this.readBytes = 0;
}
/**
* 打开文件.
*
* @param path 文件路径
* @return BufferedFile实例
*/
public static BufferedFile open(final String path) {
BufferedFile bufferFile = null;
try {
final RandomAccessFile file = new RandomAccessFile(path, "rw");
bufferFile = new BufferedFile(file);
} catch (FileNotFoundException ex) {
// 不做处理,bufferFile为null
}
return bufferFile;
}
/**
* 关闭文件.
*/
public void close() {
try {
file.close();
buffer = null;
} catch (IOException ex) {
// 不做处理
}
}
/**
* 获取文件当前偏移.
* @return 文件当前偏移量
*/
public long tell() {
return curOffset;
}
/**
* 获取当前文件长度.
* @return 当前文件长度.
*/
public long getFileSize() throws IOException {
return file.length();
}
/**
* 判断当前文件指针是否指向文件尾 EOF.
*
* @return true or false
*/
public boolean feof() throws IOException {
return this.file.length() == this.curOffset;
}
/**
* 定位到文件的指定位置.
*
* @param offset 指定文件相对于文件起始处的偏移量
*/
public void seek(final long offset) throws IOException {
file.seek(offset);
curOffset = offset;
buffer = null;
}
/**
* 从当前文件偏移,一直读到文件尾.
* @return 包含文件所有行的string list
*/
public List<String> getAllLines() throws IOException {
final List<String> lines = new LinkedList<String>();
String line;
while (true) {
line = getLine();
if (line == null) {
break;
}
lines.add(line);
}
return lines;
}
/**
* 读取文件的一行.
*
* @return String
*/
public String getLine() throws IOException {
if (buffer == null) {
buffer = new byte[BUFFER_LEN];
readBytes = file.read(buffer, 0, BUFFER_LEN);
if (readBytes <= 0) {
// end of file
return null;
}
}
String result = null;
int length = 0;
// 一行的数据都位于buffer中
// -|----|----
for (; endPos < readBytes; ++endPos) {
if (buffer[endPos] == '\n') {
++endPos;
length = endPos - startPos;
result = new String(buffer, startPos, endPos - startPos);
startPos = endPos;
curOffset += length;
return result;
}
}
// 如果一行的长度超过 1MB,直接调用 readLine 方法
// |---------- |
if (startPos == 0 && endPos == readBytes - 1) {
seek(curOffset);
result = file.readLine();
curOffset = file.getFilePointer();
startPos = endPos = 0;
buffer = null;
return result;
}
// 行尾不在buffer的情况
// -----------|--- |
file.seek(curOffset);
startPos = endPos = 0;
buffer = null;
return getLine();
}
}