【问题标题】:Efficient way of handling file pointers in Java? (Using BufferedReader with file pointer)在Java中处理文件指针的有效方法? (将 BufferedReader 与文件指针一起使用)
【发布时间】:2010-12-07 04:49:12
【问题描述】:

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

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

但是,我已经阅读了大多数文章,甚至 Java 文档建议使用 BufferredReader 来有效读取文件。如何使用BufferedReader 来实现这一点(获取文件指针并移至最后一行),或者有没有其他有效的方法来完成这项任务?

【问题讨论】:

    标签: java file pointers buffered


    【解决方案1】:

    几种应该可行的方法:

    • 使用 FileInputStream 打开文件,skip() 相关字节数,然后将 BufferedReader 包裹在流周围(通过 InputStreamReader);
    • 打开文件(使用 FileInputStream 或 RandomAccessFile),在流/RandomAccessFile 上调用 getChannel() 以获取底层 FileChannel,在通道上调用 position(),然后调用 Channels.newInputStream() 从其中获取输入流通道,您可以将其传递给 InputStreamReader -> BufferedReader。

    我并没有诚实地分析这些以查看哪个性能更好,但是您应该看看哪个在您的情况下效果更好。

    RandomAccessFile 的问题本质上是它的 readLine() 方法效率很低。如果方便您从 RAF 读取数据并进行自己的缓冲以拆分行,那么 RAF 本身并没有什么问题——只是它的 readLine() 实现不佳

    【讨论】:

      【解决方案2】:

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

      • 1) 您想从某个偏移量读取数据到当前文件长度。因此,您在 FileInputStream/FileChannel(通过 InputStreamReader)上使用 BR 并使用其 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();
      
      }
      

      【讨论】:

        【解决方案3】:

        我遇到了类似的问题,我创建了这个类来从 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;
            }
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2022-11-11
          • 1970-01-01
          • 2011-06-22
          • 1970-01-01
          • 1970-01-01
          • 2011-08-16
          • 2022-01-15
          • 2021-11-06
          相关资源
          最近更新 更多