2009-10-15 8 views
5

У меня есть файл журнала, который обновляется каждую секунду. Мне нужно периодически читать файл журнала, и как только я прочитал, мне нужно сохранить позицию указателя на файл в конце последней строки, которую я прочитал, и в следующем периодическом чтении я должен начать с этой точки.Эффективный способ обработки указателей файлов в Java? (Использование BufferedReader с указателем файла)

В настоящее время я использую файл произвольного доступа на Java и используя метод getFilePointer(), чтобы получить значение его смещения, а метод seek() - перейти в позицию смещения.

Тем не менее, я прочитал в большинстве статей и даже рекомендации Java doc использовать BufferredReader для эффективного чтения файла. Как я могу достичь этого (получение файла и переход к последней строке) с помощью BufferedReader или какой-либо другой эффективный способ для достижения этой задачи?

ответ

4

Несколько способов, которые должны работать:

  • открыть файл с помощью FileInputStream, пропустить() соответствующее число байтов, затем обернуть вокруг BufferedReader потока (через InputStreamReader);
  • открыть файл (с помощью FileInputStream или RandomAccessFile), вызвать getChannel() в потоке/RandomAccessFile, чтобы получить базовый FileChannel, позицию вызова() на канале, а затем вызвать Channels.newInputStream(), чтобы получить входной поток из канал, который вы можете передать в InputStreamReader -> BufferedReader.

Я не честно профилировал их, чтобы увидеть, что лучше по производительности, но вы должны увидеть, что лучше работает в вашей ситуации.

Проблема с RandomAccessFile заключается в том, что метод readLine() очень неэффективен. Если вам удобно читать RAF и делать свою собственную буферизацию, чтобы разделить линии, тогда нет ничего плохого в RAF как таковой, так как его readLine() плохо реализована.

1

Решение Neil Coffey хорошо, если вы читают файлы фиксированной длины. Однако для файлов с переменной длиной (данные продолжают поступать) возникают проблемы с использованием BufferedReader непосредственно в входном потоке FileInputStream или FileChannel через InputStreamReader. Для экс рассмотрят случаи

  • 1) Вы хотите читать данные из некоторого смещения текущей длиной файла. Таким образом, вы используете BR для FileInputStream/FileChannel (через InputStreamReader) и используете его метод readLine. Но пока вы заняты чтением данных, давайте скажем, что некоторые данные были добавлены, что приводит к тому, что readLine BF считывает больше данных, чем ожидалось (предыдущая длина файла)

  • 2) Вы закончили читать статьи, но когда вы пытаетесь прочитать текущая длина файла/позиция канала, некоторые данные были добавлены внезапно, что приводит к увеличению текущей длины файла/позиции канала, но вы уже прочитали меньше данных, чем это.

В обоих вышеуказанных случаях трудно понять, фактические данные вы прочитали (вы не можете просто использовать длину данных для чтения с использованием 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(); 

} 
0

у меня была аналогичная проблема, и я создал этот класс, чтобы взять строки из 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; 
    } 
} 

 Смежные вопросы

  • Нет связанных вопросов^_^