14

当事件发生时,其他人的过程是通过一次附加一行来创建一个 CSV 文件。我无法控制文件格式或其他进程,但我知道它只会追加。

在 Java 程序中,我想监视这个文件,当添加一行时,读取新行并根据内容做出反应。现在忽略 CSV 解析问题。监视文件更改并一次读取一行的最佳方法是什么?

理想情况下,这将使用标准库类。该文件很可能位于网络驱动器上,因此我想要一些坚固的东西以防故障。如果可能的话,我宁愿不使用轮询 - 我更喜欢某种阻塞解决方案。

编辑 - 鉴于标准类无法使用阻塞解决方案(感谢您的回答),最强大的轮询解决方案是什么?我不想每次都重新读取整个文件,因为它可能会变得很大。

4

7 回答 7

7

从 Java 7 开始, FileSystem 类上就有了newWatchService()方法。

但是,有一些警告:

  • 它只是 Java 7
  • 这是一种可选方法
  • 它只监视目录,因此您必须自己处理文件,并担心文件移动等

在 Java 7 之前,标准 API 是不可能的。

我尝试了以下方法(以 1 秒的间隔轮询)并且它有效(仅在处理中打印):

  private static void monitorFile(File file) throws IOException {
    final int POLL_INTERVAL = 1000;
    FileReader reader = new FileReader(file);
    BufferedReader buffered = new BufferedReader(reader);
    try {
      while(true) {
        String line = buffered.readLine();
        if(line == null) {
          // end of file, start polling
          Thread.sleep(POLL_INTERVAL);
        } else {
          System.out.println(line);
        }
      }
    } catch(InterruptedException ex) {
     ex.printStackTrace();
    }
  }

由于没有人提出使用当前生产 Java 的解决方案,我想我会添加它。如有不足请在评论中补充。

于 2009-07-02T14:24:40.700 回答
5

如果使用 WatchService 类对文件发生任何更改,您可以注册以获得文件系统的通知。这需要 Java7,这里是文档链接 http://docs.oracle.com/javase/tutorial/essential/io/notification.html

这是执行此操作的代码段:

public FileWatcher(Path dir) {
   this.watcher = FileSystems.getDefault().newWatchService();
   WatchKey key = dir.register(watcher, ENTRY_MODIFY);
}

void processEvents() {
    for (;;) {
        // wait for key to be signalled
        WatchKey key;
        try {
            key = watcher.take();
        } catch (InterruptedException x) {
            return;
        }

        for (WatchEvent<?> event : key.pollEvents()) {
            WatchEvent.Kind<?> kind = event.kind();

            if (kind == OVERFLOW) {
                continue;
            }
            // Context for directory entry event is the file name of entry
            WatchEvent<Path> ev = cast(event);
            Path name = ev.context();
            Path child = dir.resolve(name);
            // print out event
            System.out.format("%s: %s file \n", event.kind().name(), child);
        }
        // reset key and remove from set if directory no longer accessible
        boolean valid = key.reset();
    }
}
于 2011-12-12T19:04:23.570 回答
3

这对于标准库类是不可能的。有关详细信息,请参阅此问题

为了进行有效的轮询,最好使用Random Access。如果您记住文件最后一个结尾的位置并从那里开始阅读,这将有所帮助。

于 2009-07-02T09:14:57.770 回答
3

使用 Java 7 的WatchService,NIO.2 的一部分

WatchService API 专为需要通知文件更改事件的应用程序而设计。

于 2009-07-02T10:41:13.053 回答
2

只是为了扩展 Nick Fortescue 的最后一个条目,下面是两个可以同时运行的类(例如在两个不同的 shell 窗口中),这表明一个给定的文件可以同时被一个进程写入并被另一个进程读取。

在这里,两个进程将执行这些 Java 类,但我假设编写进程可能来自任何其他应用程序。(假设它没有对文件持有排他锁——在某些操作系统上是否有这样的文件系统锁?)

我已经在 Windoze 和 Linux 上成功地测试了这两个类。我非常想知道是否有某些条件(例如操作系统)导致它们失败。

第一类:

import java.io.File;
import java.io.FileWriter;
import java.io.PrintWriter;

public class FileAppender {

    public static void main(String[] args) throws Exception {
        if ((args != null) && (args.length != 0)) throw
            new IllegalArgumentException("args is not null and is not empty");

        File file = new File("./file.txt");
        int numLines = 1000;
        writeLines(file, numLines);
    }

    private static void writeLines(File file, int numLines) throws Exception {
        PrintWriter pw = null;
        try {
            pw = new PrintWriter( new FileWriter(file), true );
            for (int i = 0; i < numLines; i++) {
                System.out.println("writing line number " + i);
                pw.println("line number " + i);
                Thread.sleep(100);
            }
        }
        finally {
            if (pw != null) pw.close();
        }
    }

}

第 2 类:

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;

public class FileMonitor {

    public static void main(String[] args) throws Exception {
        if ((args != null) && (args.length != 0)) throw
            new IllegalArgumentException("args is not null and is not empty");

        File file = new File("./file.txt");
        readLines(file);
    }

    private static void readLines(File file) throws Exception {
        BufferedReader br = null;
        try {
            br = new BufferedReader( new FileReader(file) );
            while (true) {
                String line = br.readLine();
                if (line == null) { // end of file, start polling
                    System.out.println("no file data available; sleeping..");
                    Thread.sleep(2 * 1000);
                }
                else {
                    System.out.println(line);
                }
            }
        }
        finally {
            if (br != null) br.close();
        }
    }

}
于 2010-02-11T15:51:53.503 回答
2

遗憾的是,可用于监控文件结尾的 TailInputStream 类不是标准的 Java 平台类之一,但在 Web 上实现的很少。您可以在http://www.greentelligent.com/java/tailinputstream上找到 TailInputStream 类的实现以及使用示例。

于 2011-04-06T21:41:18.200 回答
0

轮询,在一致周期或随机周期上;200-2000ms 应该是一个很好的随机轮询间隔跨度。

检查两件事...

如果您必须注意文件增长,请检查 EOF / 字节数,并确保将其与 fileAccess 或 fileWrite 时间与 lass poll 进行比较。如果 ( > ),则文件已被写入。

然后,将其与检查排他锁/读访问结合起来。如果文件可以被读锁定并且它已经增长,那么写入它的任何内容都已经完成。

Checking for either property alone won't necessarily get you a guaranteed state of written++ and actually done and available for use.

于 2015-01-15T16:45:04.870 回答