其他人的过程是在事件发生时通过一次添加一行来创建CSV文件。我无法控制文件格式或其他进程,但我知道它只会附加。
在Java程序中,我想监视这个文件,当附加一行时,读取新行并根据内容做出反应。暂时忽略CSV解析问题。监视文件以进行更改并一次读取一行的最佳方法是什么?
理想情况下,这将使用标准库类。该文件可能在网络驱动器上,所以我想要一些强大的失败。如果可能的话,我宁愿不使用轮询 - 我更喜欢某种阻塞解决方案。
编辑 - 鉴于标准类无法实现阻塞解决方案(感谢您的回答),最强大的轮询解决方案是什么?我不想每次重读整个文件,因为它可能会变得非常大。
答案 0 :(得分:7)
自Java 7以来newWatchService()上有FileSystem class方法。
然而,有一些警告:
在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的解决方案我以为我会添加它。如果有缺陷,请在评论中添加。
答案 1 :(得分: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();
}
}
答案 2 :(得分:3)
使用Java 7的WatchService,它是NIO.2的一部分
WatchService API专为需要通知文件更改事件的应用程序而设计。
答案 3 :(得分:2)
标准库类无法实现这一点。有关详细信息,请参阅此question。
为了进行有效的轮询,最好使用Random Access。如果你记住文件最后一端的位置并从那里开始阅读,它会有所帮助。
答案 4 :(得分:2)
为了扩展Nick Fortescue的最后一个条目,下面是两个可以同时运行的类(例如,在两个不同的shell窗口中),这表明给定的文件可以同时由一个进程写入并由另一个进程读取。
这里,两个进程将执行这些Java类,但我认为编写过程可以来自任何其他应用程序。 (假设它没有对文件进行独占锁定 - 某些操作系统上是否存在此类文件系统锁定?)
我已经在Windoze和Linux上成功测试了这两个类。我非常想知道是否存在一些失败的情况(例如操作系统)。
第1课:
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();
}
}
}
答案 5 :(得分:1)
不幸的是,可用于监视文件末尾的TailInputStream类不是标准Java平台类之一,但Web上的实现很少。您可以在http://www.greentelligent.com/java/tailinputstream上找到TailInputStream类的实现以及用法示例。
答案 6 :(得分:0)
轮询,无论是一致周期还是随机周期; 200-2000ms应该是一个很好的随机轮询间隔跨度。
检查两件事......
如果你必须注意文件增长,那么检查EOF /字节数,并确保将其与fileAccess或fileWrite时间与lass poll进行比较。如果(&gt;),那么文件已被写入。
然后,将其与检查独占锁定/读取访问权限相结合。如果文件可以被读锁定并且它已经增长,那么写入它的任何内容都已完成。
单独检查任何一个属性都不一定能让你保证写入++ 和的实际状态并且可以使用。