2009-10-15 49 views
5

Tôi có một log file đó được cập nhật mỗi giây. Tôi cần phải đọc tệp nhật ký định kỳ, và một khi tôi đọc, tôi cần phải lưu trữ vị trí con trỏ tệp ở cuối dòng cuối cùng mà tôi đọc và trong lần đọc tiếp theo tôi nên bắt đầu từ điểm đó.Cách hiệu quả để xử lý các con trỏ tệp trong Java? (Sử dụng BufferedReader với con trỏ tập tin)

Hiện tại, tôi đang sử dụng tệp truy cập ngẫu nhiên trong Java và sử dụng phương thức getFilePointer() để nhận giá trị bù của anh ấy và phương thức seek() để chuyển đến vị trí bù trừ.

Tuy nhiên, tôi đã đọc trong hầu hết các bài báo và thậm chí các khuyến nghị doc Java để sử dụng BufferredReader cho việc đọc hiệu quả của một tập tin. Làm thế nào tôi có thể đạt được điều này (nhận được filepointer và di chuyển đến dòng cuối cùng) sử dụng một BufferedReader, hoặc là có cách nào hiệu quả khác để đạt được nhiệm vụ này?

Trả lời

4

Một vài cách mà nên làm việc:

  • hãy mở tệp bằng một FileInputStream, skip() số liên quan của byte, sau đó quấn xung quanh BufferedReader dòng (thông qua một InputStreamReader);
  • mở tệp (bằng FileInputStream hoặc RandomAccessFile), gọi getChannel() trên luồng/RandomAccessFile để lấy FileChannel bên dưới, gọi vị trí() trên kênh, sau đó gọi Channels.newInputStream() để nhận luồng đầu vào từ kênh mà bạn có thể chuyển tới InputStreamReader -> BufferedReader.

Tôi chưa phân tích một cách trung thực những thứ này để xem cái nào hiệu quả hơn, nhưng bạn nên xem cái nào hoạt động tốt hơn trong tình huống của mình.

Vấn đề với RandomAccessFile thực chất là phương pháp readLine() của nó là rất không hiệu quả. Nếu nó thuận tiện cho bạn đọc từ RAF và làm đệm của riêng bạn để phân chia các dòng, thì không có gì sai với RAF mỗi lần - chỉ rằng readLine() của nó được thực hiện kém

1

Giải pháp của Neil Coffey là tốt nếu bạn đang đọc các tệp có độ dài cố định. Tuy nhiên đối với các tệp có độ dài biến đổi (dữ liệu tiếp tục đến), có một số vấn đề khi sử dụng BufferedReader trực tiếp trên InputInInStream hoặc FileChannel thông qua InputStreamReader. Ví dụ: hãy xem xét các trường hợp

  • 1) Bạn muốn đọc dữ liệu từ một số bù đắp cho độ dài tệp hiện tại. Vì vậy, bạn sử dụng BR trên FileInputStream/FileChannel (thông qua InputStreamReader) và sử dụng phương thức readLine của nó. Nhưng trong khi bạn đang bận đọc dữ liệu hãy nói rằng một số dữ liệu đã được thêm vào làm cho readLine của BF đọc nhiều dữ liệu hơn so với những gì bạn mong đợi (độ dài tệp trước)

  • 2) Bạn đã hoàn thành công cụ readLine nhưng khi bạn cố đọc chiều dài tập tin hiện tại/vị trí kênh một số dữ liệu đã được thêm đột ngột gây ra chiều dài tập tin hiện tại/vị trí kênh để tăng nhưng bạn đã đọc ít dữ liệu hơn điều này.

Trong cả hai trường hợp trên rất khó để biết các dữ liệu thực tế bạn đã đọc (bạn không thể chỉ sử dụng chiều dài của dữ liệu đọc sử dụng readLine vì nó bỏ qua một số ký tự như vận chuyển trở lại)

Vì vậy, nó là tốt hơn để đọc dữ liệu trong byte đệm và sử dụng một wrapper BufferedReader xung quanh này.Tôi đã viết một số phương pháp như thế này

/** 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(); 

} 
0

Tôi đã có một vấn đề tương tự, và tôi tạo ra lớp này có dòng từ BufferedStream, và đếm có bao nhiêu byte bạn đã đọc cho đến nay bằng cách sử dụng getBytes(). Chúng tôi giả định bộ tách dòng có một byte đơn theo mặc định và chúng tôi sẽ hiển thị lại BufferedReader cho seek() để hoạt động.

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; 
    } 
} 
Các vấn đề liên quan