我目前正在开发一个管理远程minecraft服务器的应用程序,我正在做的第一件事就是观察minecraft控制台的消息。我通过观察minecraft服务器复制其输出的最新.log文本文件来做到这一点。日志消息每条添加到一个换行中,因此监视的基本工作流是:
start -> read all existing lines of latest.log -> watch for file change notifications on latest.log ->
read newly added line(s) -> wait for next file change notification
我实现了下面的类来做到这一点:
public class McServerService {
private String directory;
private List<String> currentLog;
private Thread logObserverThread;
private PropertyChangeSupport pcsupport;
public McServerService (String directory) {
this.currentLog = new ArrayList<String>();
this.directory = directory;
this.pcsupport = new PropertyChangeSupport(this);
}
public void startWatching () {
this.logObserverThread = new Thread(new LogObserverThreadImpl(this.directory));
this.logObserverThread.start();
}
public void addNewLogLine (String newLogLine) {
this.pcsupport.firePropertyChange("currentLog", this.currentLog, newLogLine);
this.currentLog.add(newLogLine);
System.out.println("addNewLogLine: " + newLogLine);
}
public void addPropertyChangeListener (PropertyChangeListener pcl) {
this.pcsupport.addPropertyChangeListener(pcl);
}
public void removePropertyChangeListener (PropertyChangeListener pcl) {
this.pcsupport.removePropertyChangeListener(pcl);
}
private class LogObserverThreadImpl implements Runnable {
BufferedReader br;
WatchService watchService;
private LogObserverThreadImpl (String directory) {
try {
this.br = new BufferedReader(new java.io.FileReader(directory + "\" + "latest.log"));
String nextLine = this.br.readLine();
while (nextLine != null) {
McServerService.this.currentLog.add(nextLine);
System.out.println("init: " + nextLine);
this.br.mark(2048);
nextLine = this.br.readLine();
System.out.println("init: " + nextLine);
}
this.br.reset();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public void run() {
Path path = Paths.get(directory);
try {
System.out.println("entered try");
this.watchService = FileSystems.getDefault().newWatchService();
path.register(watchService, StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_MODIFY);
WatchKey key;
while ((key = this.watchService.take()) != null) {
for (WatchEvent<?> event : key.pollEvents()) {
if (event.context().toString().equals("latest.log")) {
String line = this.br.readLine();
/*
* if (line.equalsIgnoreCase("")) { line = this.br.readLine(); }
*/
McServerService.this.addNewLogLine(line);
System.out.println("thread: " + line);
}
}
key.reset();
}
System.out.println("after while");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
现有的最新的.log文本文件将按照预期读取其所有行,然后以空结束,但是当添加两行
时gadhsjlhgadsjlkh
jlhkadshljhads
并将文件保存在每行之后,输出如下所示:
init: null //"last" line of existing file
entered try
//now adding the two lines
pclimlp:
addNewLogLine:
thread:
pclimlp: gadhsjlhgadsjlkh
addNewLogLine: gadhsjlhgadsjlkh
thread: gadhsjlhgadsjlkh
这个问题可以通过取消"等号检查的注释来解决。:
while ((key = this.watchService.take()) != null) {
for (WatchEvent<?> event : key.pollEvents()) {
if (event.context().toString().equals("latest.log")) {
String line = this.br.readLine();
/*
* if (line.equalsIgnoreCase("")) { line = this.br.readLine(); }
*/
McServerService.this.addNewLogLine(line);
System.out.println("thread: " + line);
}
}
key.reset();
}
但是为什么bufferedreader首先读取空行呢?我理解null,如果BufferedReader还没有更新,但为什么空行?此外,如果我再次手动保存文件(不添加更多行或任何东西),BufferedReader将读取之前没有读取的行,并且不会跳过它。有人能解释一下是什么导致了这种行为吗?
bufferedread . readline()读取到下一个换行符或文件结束。
所以当读取这个文件时:
[......]
[21:34:31] [Server thread/INFO]: [removed] left the game
BufferedReader给出如下输出:
[......]
init: [21:34:31] [Server thread/INFO]: [removed] left the game
init: null
"init:";仅为调试目的而添加。当附加
xx
ghasjdkhajs
所以文件的结尾看起来像这样:
[21:34:31] [Server thread/INFO]: [removed] left the gamexx
ghasjdkhajs
这将导致以下输出:
pclimlp: xx
addNewLogLine: xx
thread: xx
所以BufferedReader在文件的末尾就在
之后left the game
所以当添加
xxrnghasjdkhajs
BufferedReader读取到下一个换行符。如果没有"xx"添加之前是没有下一个换行,所以BufferedReader将返回"就像刚才那个问题。