Javaを使用して、アクティブに書き込まれているファイルから読み取るにはどうすればよいですか?


99

ファイルに情報を書き込むアプリケーションがあります。この情報は、実行後にアプリケーションの合格/失敗/正確さを判断するために使用されます。書き込み中にファイルを読み取れるようにして、これらの合格/失敗/正確性チェックをリアルタイムで実行できるようにしたいと考えています。

私はこれが可能だと思いますが、Javaを使用する場合の落とし穴は何ですか?読み取りが書き込みに追いついた場合、ファイルが閉じられるまでさらに書き込みが行われるのを待つだけですか、それとも読み取りがこの時点で例外をスローしますか?後者の場合、どうしたらいいですか?

私の直感は現在、BufferedStreamsに向かっています。これは行く方法ですか?


1
ねえ、私は同様のシナリオに直面しているので、あなたが受け入れられたものよりも良い解決策を見つけた場合、私は言いましたか?
Asaf David

これは古い質問であることは承知していますが、将来の読者のために、ユースケースをもう少し詳しく説明していただけますか?より多くの情報がなければ、おそらく間違った問題を解決しているのではないでしょうか。
user359996 2010年

Apache Commons IOからTailerを使用することを検討してください。エッジケースのほとんどを処理します。
ジョシュア

2
データベースを使用します。これらの「書き込まれている間にファイルを読み取る」シナリオは、涙で終わります。
ローン侯爵

@EJP-どのDBをお勧めしますか?MySQLは良いスタートだと思いますか?
コーヒー

回答:


39

FileChannel.read(ByteBuffer)これはブロッキング読み取りではないため、使用例を使用できませんでした。ただし、以下のコードが機能するようになりましたか。

boolean running = true;
BufferedInputStream reader = new BufferedInputStream(new FileInputStream( "out.txt" ) );

public void run() {
    while( running ) {
        if( reader.available() > 0 ) {
            System.out.print( (char)reader.read() );
        }
        else {
            try {
                sleep( 500 );
            }
            catch( InterruptedException ex ) {
                running = false;
            }
        }
    }
}

もちろん、同じことはスレッドではなくタイマーとして機能しますが、プログラマーに任せます。まだもっと良い方法を探していますが、今のところこれでうまくいきます。

ああ、私はこれを注意します:1.4.2を使用しています。はい、私はまだ石器時代であることを知っています。


1
これを追加してくれてありがとう... ファイルをロックするというブレードの答えも良いものだと思います。ただし、Java 6が必要です(私はそう思います)。
Anthony Cramp

@JosephGordon-これらの日のうちの1つでドローン時代に移行する必要があります;-)
TungstenX

12

ファイルの書き込み中にファイルを読み取り、新しいコンテンツのみを読み取りたい場合は、次の手順で同じことができます。

このプログラムを実行するには、コマンドプロンプト/ターミナルウィンドウから起動し、読み取るファイル名を渡します。プログラムを強制終了しない限り、ファイルを読み取ります。

java FileReader c:\ myfile.txt

テキスト行を入力するときにメモ帳から保存すると、コンソールに印刷されたテキストが表示されます。

public class FileReader {

    public static void main(String args[]) throws Exception {
        if(args.length>0){
            File file = new File(args[0]);
            System.out.println(file.getAbsolutePath());
            if(file.exists() && file.canRead()){
                long fileLength = file.length();
                readFile(file,0L);
                while(true){

                    if(fileLength<file.length()){
                        readFile(file,fileLength);
                        fileLength=file.length();
                    }
                }
            }
        }else{
            System.out.println("no file to read");
        }
    }

    public static void readFile(File file,Long fileLength) throws IOException {
        String line = null;

        BufferedReader in = new BufferedReader(new java.io.FileReader(file));
        in.skip(fileLength);
        while((line = in.readLine()) != null)
        {
            System.out.println(line);
        }
        in.close();
    }
}

2
ファイルが読み取られてからファイルの長さが取得されるまでの間に、外部プロセスがファイルにデータを追加する可能性はありませんか。その場合、これにより、読み取りプロセスでファイルに書き込まれたデータが失われます。
JohnC

1
ループにはthread.sleep呼び出しがないため、このコードは大量のCPUを消費します。少しの遅延を追加しないと、このコードはCPUを非常にビジー状態に保つ傾向があります。
ChaitanyaBhatt

上記の両方のコメントは真実であり、さらにBufferedReader各ループ呼び出しでのこの作成は非常に無意味です。バッファリーダーは新しい行が到着したときにそれを読み取るため、一度作成するとスキップする必要はありません。
Piotr


5

ジョシュアの返答に完全に同意します、テイラーます。この状況でははこの仕事に適しています。次に例を示します。

ファイルに150ミリ秒ごとに1行書き込み、2500ミリ秒ごとにこの非常に同じファイルを読み取る

public class TailerTest
{
    public static void main(String[] args)
    {
        File f = new File("/tmp/test.txt");
        MyListener listener = new MyListener();
        Tailer.create(f, listener, 2500);

        try
        {
            FileOutputStream fos = new FileOutputStream(f);
            int i = 0;
            while (i < 200)
            {
                fos.write(("test" + ++i + "\n").getBytes());
                Thread.sleep(150);
            }
            fos.close();
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }

    private static class MyListener extends TailerListenerAdapter
    {
        @Override
        public void handle(String line)
        {
            System.out.println(line);
        }
    }
}

Tailerへのリンクが壊れています。
ステルスラビ

2
@StealthRabbi次に行うべき最善のことは、正しいリンクを検索し、それを使用して回答を編集することです。
igracia 2016年

3

答えは「いいえ」と「はい」のようです。ファイルが別のアプリケーションによって書き込み用に開かれているかどうかを確認する実際の方法はないようです。したがって、そのようなファイルからの読み取りは、コンテンツがなくなるまで進行します。私はマイクのアドバイスを受けて、いくつかのテストコードを書きました。

Writer.javaは文字列をファイルに書き込み、ユーザーがEnterキーを押すのを待ってから、別の行をファイルに書き込みます。起動することができるという考えから、リーダーを起動して、「部分」ファイルへの対処方法を確認できます。私が書いたリーダーはReader.javaにあります。

Writer.java

public class Writer extends Object
{
    Writer () {

    }

    public static String[] strings = 
        {
            "Hello World", 
            "Goodbye World"
        };

    public static void main(String[] args) 
        throws java.io.IOException {

        java.io.PrintWriter pw =
            new java.io.PrintWriter(new java.io.FileOutputStream("out.txt"), true);

        for(String s : strings) {
            pw.println(s);
            System.in.read();
        }

        pw.close();
    }
}

Reader.java

public class Reader extends Object
{
    Reader () {

    }

    public static void main(String[] args) 
        throws Exception {

        java.io.FileInputStream in = new java.io.FileInputStream("out.txt");

        java.nio.channels.FileChannel fc = in.getChannel();
        java.nio.ByteBuffer bb = java.nio.ByteBuffer.allocate(10);

        while(fc.read(bb) >= 0) {
            bb.flip();
            while(bb.hasRemaining()) {
                System.out.println((char)bb.get());
            }
            bb.clear();
        }

        System.exit(0);
    }
}

このコードがベストプラクティスである保証はありません。

これにより、ファイルから読み取る新しいデータがあるかどうかを定期的にチェックするというMikeの提案オプションが残ります。これにより、読み取りが完了したと判断されたときにファイルリーダーを閉じるためにユーザーの介入が必要になります。または、リーダーはファイルの内容を認識し、書き込みの終了条件を判別できる必要があります。コンテンツがXMLの場合、ドキュメントの終わりを使用してこれを示すことができます。


1

Java自体ではありませんが、ファイルに何かを書き込んだが、実際にはまだ書き込まれていないという問題が発生する可能性があります-どこかのキャッシュにある可能性があり、同じファイルからの読み取りでは実際には得られない場合があります新しい情報。

短いバージョン-flush()または関連するシステムコールを使用して、データが実際にファイルに書き込まれるようにします。

注:私はOSレベルのディスクキャッシュについて話しているのではありません。データがここに入る場合は、この時点でread()に表示されるはずです。言語自体が書き込みをキャッシュし、バッファがいっぱいになるかファイルがフラッシュ/クローズされるまで待機している可能性があります。


1

これを行うオープンソースのJavaグラフィックテールがあります。

https://stackoverflow.com/a/559146/1255493

public void run() {
    try {
        while (_running) {
            Thread.sleep(_updateInterval);
            long len = _file.length();
            if (len < _filePointer) {
                // Log must have been jibbled or deleted.
                this.appendMessage("Log file was reset. Restarting logging from start of file.");
                _filePointer = len;
            }
            else if (len > _filePointer) {
                // File must have had something added to it!
                RandomAccessFile raf = new RandomAccessFile(_file, "r");
                raf.seek(_filePointer);
                String line = null;
                while ((line = raf.readLine()) != null) {
                    this.appendLine(line);
                }
                _filePointer = raf.getFilePointer();
                raf.close();
            }
        }
    }
    catch (Exception e) {
        this.appendMessage("Fatal error reading log file, log tailing has stopped.");
    }
    // dispose();
}

1

FileInputStream、FileReader、またはRandomAccessFileを使用して別のプロセスから開かれたファイルを読み取ることはできません。

ただし、FileChannelを直接使用すると機能します。

private static byte[] readSharedFile(File file) throws IOException {
    byte buffer[] = new byte[(int) file.length()];
    final FileChannel fc = FileChannel.open(file.toPath(), EnumSet.of(StandardOpenOption.READ));
    final ByteBuffer dst = ByteBuffer.wrap(buffer);
    fc.read(dst);
    fc.close();
    return buffer;
}

0

私は試したことはありませんが、ファイルにさらにデータが書き込まれているかどうかに関係なく、最後に到達した後のストリームからの読み取りが機能するかどうかを確認するテストケースを作成する必要があります。

パイプされた入出力ストリームを使用できない理由はありますか?同じアプリケーションからデータの書き込みと読み取りが行われていますか(ある場合、データがあるのに、なぜファイルから読み取る必要があるのですか)。

それ以外の場合は、ファイルの終わりまで読んでから変更を監視し、中断したところから続行してください。ただし、競合状態に注意してください。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.