5

毎秒更新されるログ ファイルがあります。ログファイルを定期的に読み取る必要があり、読み取りを行ったら、最後に読み取った行の最後にファイルポインターの位置を保存する必要があり、次の定期的な読み取りではその時点から開始する必要があります。

現在、私はJavaでランダムアクセスファイルを使用しておりgetFilePointer()、オフセット値を取得するseek()メソッドとオフセット位置に移動するメソッドを使用しています。

ただし、ほとんどの記事とBufferredReader、ファイルを効率的に読み取るために使用する Java ドキュメントの推奨事項を読みました。を使用してこれを達成するにはどうすればよいですか (ファイルポインタを取得して最後の行に移動する) BufferedReader、またはこのタスクを達成するための他の効率的な方法はありますか?

4

3 に答える 3

4

動作するはずのいくつかの方法:

  • FileInputStream を使用してファイルを開き、関連するバイト数をスキップしてから、(InputStreamReader を介して) BufferedReader をストリームにラップします。
  • ファイルを (FileInputStream または RandomAccessFile で) 開き、ストリーム/RandomAccessFile で getChannel() を呼び出して基になる FileChannel を取得し、チャネルで position() を呼び出し、次に Channels.newInputStream() を呼び出してチャネルから入力ストリームを取得します。これを InputStreamReader -> BufferedReader に渡すことができます。

どちらがパフォーマンス的に優れているかを確認するために、これらを正直にプロファイルしていませんが、状況に応じてどちらがうまく機能するかを確認する必要があります。

RandomAccessFile の問題は、本質的にその readLine() メソッドが非常に非効率的であることです。RAF から読み取り、独自のバッファリングを行って行を分割するのが便利な場合は、RAF 自体に問題はありません。readLine() の実装が不十分なだけです。

于 2009-10-15T21:22:00.157 に答える
1

固定長のファイルを読み取る場合は、Neil Coffey のソリューションが適しています。ただし、可変長のファイル (データが入り続ける) の場合、InputStreamReader を介して FileInputStream または FileChannel 入力ストリームで BufferedReader を直接使用すると、いくつかの問題が発生します。たとえば、ケースを検討してください

  • 1) あるオフセットから現在のファイル長までのデータを読み取りたい。したがって、(InputStreamReader を介して) FileInputStream/FileChannel で 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();

}
于 2013-11-08T19:54:40.250 に答える
0

同様の問題があり、このクラスを作成して BufferedStream から行を取得し、これまでに読み取ったバイト数をgetBytes(). BufferedReader行区切りはデフォルトで 1 バイトであると想定し、 forを再インスタンス化し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;
    }
}
于 2015-04-11T17:48:23.587 に答える