我如何使用Java从正在写入的文件中读取数据?

我有一个将信息写入文件的应用程序。此信息在执行后用于确定应用程序的通过/失败/正确性。我希望能够在写入文件时阅读该文件,以便我可以实时进行这些通过/失败/正确性检查。

我认为有可能做到这一点,但使用Java时涉及的问题有哪些?如果读取到达写入,它是否会等待更多的写入,直到文件关闭,或读取是否会在此处引发异常?如果是后者,那我该怎么办?

我的直觉正在将我推向BufferedStreams。这是要走的路吗?

0
@EJP - 你推荐哪个数据库?我猜MySQL是一个好的开始?
额外 作者 Coffee,
使用数据库。这些'在写入文件时读取文件'的情景最终流下了眼泪。
额外 作者 EJP,
嘿,因为我面临着类似的情况,如果你找到比接受的解决方案更好的解决方案,我的措辞是?
额外 作者 Asaf David,
我知道这是一个古老的问题,但为了未来的读者,你可以扩展你的用例吗?没有更多的信息,人们会想知道你是否可能解决了错误的问题。
额外 作者 user359996,

10 答案

我从来没有尝试过,但是你应该编写一个测试用例,看看在结束后读取流是否有效,无论是否有更多的数据写入文件。

有没有理由不能使用管道输入/输出流?数据是从同一个应用程序写入和读取的(如果是这样,你有数据,为什么你需要从文件中读取)?

否则,可能会读到文件结束,然后监视变化并寻找你离开的地方并继续......但注意比赛条件。

0
额外

不是Java本身,但是你可能会碰到你写了一些文件的问题,但是它还没有真正写入 - 它可能在某处的缓存中,而从同一个文件中读取可能实际上并不会给你新的信息。

短版本 - 使用flush()或任何相关的系统调用来确保您的数据实际写入文件。

注意我不是在谈论操作系统级别的磁盘缓存 - 如果你的数据进入这里,它应该在这个点之后出现在read()中。这可能是语言本身缓存写入,等待缓冲区填满或文件被刷新/关闭。

0
额外

答案似乎是“不”......和“是”。似乎没有真正的方法来知道一个文件是否被另一个应用程序写入。因此,从这样的文件中读取内容只会耗尽内容。我接受了迈克的建议并编写了一些测试代码:

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,则文档的结尾可以用来指示这一点。

0
额外

你也可以看看java通道来锁定文件的一部分。

http://java.sun.com/javase /6/docs/api/java/nio/channels/FileChannel.html

FileChannel 的这个函数可能是一个开始

lock(long position, long size, boolean shared) 

此方法的调用将阻塞,直到该区域可被锁定

0
额外

考虑使用 Tailer 来自Apache Commons IO。它处理大多数边缘情况。

0
额外

我完全同意约书亚的回应Tailer 适合这种情况。这里是一个例子:

它每隔150毫秒在一个文件中写一行,而每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);
        }
    }
}
0
额外
您与Tailer的链接已中断。
额外 作者 Stealth Rabbi,
@StealthRabbi那么最好的做法是搜索正确的链接,并用它编辑答案。
额外 作者 igracia,

您无法读取使用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
额外

无法让示例使用 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。是的,我知道我还在石器时代。

0
额外
感谢您添加这个...我从来没有做过的事情。我认为Blade的锁定文件的答案也很好。但是,它需要Java 6(我认为)。
额外 作者 Anthony Cramp,
@AnthonyCramp你为什么不给予赏金? :)
额外 作者 Yassin Hajaj,
@JosephGordon--你必须在这些日子中转移到无人机时代;-)
额外 作者 TungstenX,

有一个开源的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();
}
0
额外

如果您要在写入文件时阅读文件,并只阅读新内容,则以下内容将帮助您达到相同效果。

要运行此程序,您将从命令提示符/终端窗口启动它并传递文件名以进行读取。它会读取文件,除非你杀了程序。

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
0
额外
此代码消耗大量的CPU,因为循环中没有thread.sleep调用。在不增加少量延迟的情况下,这些代码会使CPU非常忙碌。
额外 作者 ChaitanyaBhatt,
在文件被读取和文件长度被占用的时间之间不存在这种可能性,外部进程可以向文件添加更多数据。如果是这样,这将导致读取过程丢失写入文件的数据。
额外 作者 JohnC,