在Java中处理文件指针的有效方法? (使用带文件指针的BufferedReader)

我有一个日志文件,每秒都会更新。 我需要定期读取日志文件,一旦我读取,我需要将文件指针位置存储在我读取的最后一行的末尾,并且在下一次定期读取时我应该从该点开始。

目前,我使用Java中的随机访问文件,并使用getFilePointer()方法获取偏移值,并使用seek()方法转到偏移位置。

但是,我已阅读大多数文章甚至Java doc建议,以使用BufferredReader有效读取文件。 如何使用BufferedReader实现此目的(获取文件指针并移动到最后一行),还是有其他有效的方法来实现此任务?

有两种方法可行:

  • 使用FileInputStream打开文件,skip()相关的字节数,然后围绕流包装BufferedReader(通过InputStreamReader);
  • 打开文件(使用FileInputStream或RandomAccessFile),在流/ RandomAccessFile上调用getChannel()以获取基础FileChannel,在通道上调用position(),然后调用Channels.newInputStream()从通道获取输入流,你可以传递给InputStreamReader – > BufferedReader。

我没有老实地描述这些,看看哪个性能更好,但你应该看看哪种情况更适合你的情况。

RandomAccessFile的问题本质上是它的readLine()方法非常低效。 如果您方便地从RAF读取并自行缓冲以分割线路,那么RAF本身没有任何问题 – 只是它的readLine()执行得很差

如果您正在阅读固定长度的文件,Neil Coffey的解决方案很好。 但是对于具有可变长度的文件(数据不断进入),通过InputStreamReader直接在FileInputStream或FileChannel输入流上使用BufferedReader存在一些问题。 如需考虑案例

  • 1)您想要从某个偏移量读取数据到当前文件长度。 因此,您在FileInputStream / FileChannel上使用BR(通过InputStreamReader)并使用其readLine方法。 但是当你忙于读取数据时,请说添加了一些数据,这会导致BF的readLine读取比预期更多的数据(之前的文件长度)

  • 2)你完成了readLine的东西,但当你试图读取当前文件长度/通道位置时,一些数据突然添加,导致当前文件长度/通道位置增加,但你已经读取了比这更少的数据。

在上述两种情况下,很难知道你读过的实际数据(你不能只使用readLine读取的数据长度,因为它会跳过一些字符,如回车)

因此,最好以缓冲字节读取数据,并使用BufferedReader包装器。 我写了一些像这样的方法

 /** Read data from offset to length bytes in RandomAccessFile using BufferedReader * @param offset * @param length * @param accessFile * @throws IOException */ public static void readBufferedLines(long offset, long length, RandomAccessFile accessFile) throws IOException{ if(accessFile == null) return; int bufferSize = BYTE_BUFFER_SIZE;// constant say 4096 if(offset < length && offset >= 0){ int index = 1; long curPosition = offset; /* * iterate (length-from)/BYTE_BUFFER_SIZE times to read into buffer no matter where new line occurs */ while((curPosition + (index * BYTE_BUFFER_SIZE)) < length){ accessFile.seek(offset); // seek to last parsed data rather than last data read in to buffer byte[] buf = new byte[bufferSize]; int read = accessFile.read(buf, 0, bufferSize); index++;// Increment whether or not read successful if(read > 0){ int lastnewLine = getLastLine(read,buf); if(lastnewLine <= 0){ // no new line found in the buffer reset buffer size and continue bufferSize = bufferSize+read; continue; } else{ bufferSize = BYTE_BUFFER_SIZE; } readLine(buf, 0, lastnewLine); // read the lines from buffer and parse the line offset = offset+lastnewLine; // update the last data read } } // Read last chunk. The last chunk size in worst case is the total file when no newline occurs if(offset < length){ accessFile.seek(offset); byte[] buf = new byte[(int) (length-offset)]; int read = accessFile.read(buf, 0, buf.length); if(read > 0){ readLine(buf, 0, read); offset = offset+read; // update the last data read } } } } private static void readLine(byte[] buf, int from , int lastnewLine) throws IOException{ String readLine = ""; BufferedReader reader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(buf,from,lastnewLine) )); while( (readLine = reader.readLine()) != null){ //do something with readLine System.out.println(readLine); } reader.close(); } private static int getLastLine(int read, byte[] buf) { if(buf == null ) return -1; if(read > buf.length) read = buf.length; while( read > 0 && !(buf[read-1] == '\n' || buf[read-1] == '\r')) read--; return read; } public static void main(String[] args) throws IOException { RandomAccessFile accessFile = new RandomAccessFile("C:/sri/test.log", "r"); readBufferedLines(0, accessFile.length(), accessFile); accessFile.close(); } 

我遇到了类似的问题,我创建了这个类来从BufferedStream中获取行,并使用getBytes()到目前为止已读取的字节数。 我们假设行分隔符默认具有单个字节,并且我们重新实例化了BufferedReader以使seek()起作用。

 public class FileCounterIterator { public Long position() { return _position; } public Long fileSize() { return _fileSize; } public FileCounterIterator newlineLength(Long newNewlineLength) { this._newlineLength = newNewlineLength; return this; } private Long _fileSize = 0L; private Long _position = 0L; private Long _newlineLength = 1L; private RandomAccessFile fp; private BufferedReader itr; public FileCounterIterator(String filename) throws IOException { fp = new RandomAccessFile(filename, "r"); _fileSize = fp.length(); this.seek(0L); } public FileCounterIterator seek(Long newPosition) throws IOException { this.fp.seek(newPosition); this._position = newPosition; itr = new BufferedReader(new InputStreamReader(new FileInputStream(fp.getFD()))); return this; } public Boolean hasNext() throws IOException { return this._position < this._fileSize; } public String readLine() throws IOException { String nextLine = itr.readLine(); this._position += nextLine.getBytes().length + _newlineLength; return nextLine; } }