我有一个应用程序,它会将信息写入文件。这些信息在执行后用于确定应用程序的通过/失败/正确性。我希望能够在写入文件时读取文件,以便我可以实时进行这些通过/失败/正确性检查。
我认为这是可能的,但使用Java时会有什么问题?如果读取赶上了写入,它会一直等待更多的写入直到文件关闭,还是在此时读取将抛出异常?如果是后者,那我该怎么办?
我的直觉目前正在推动我使用BufferedStreams。这是正确的方法吗?
我有一个应用程序,它会将信息写入文件。这些信息在执行后用于确定应用程序的通过/失败/正确性。我希望能够在写入文件时读取文件,以便我可以实时进行这些通过/失败/正确性检查。
我认为这是可能的,但使用Java时会有什么问题?如果读取赶上了写入,它会一直等待更多的写入直到文件关闭,还是在此时读取将抛出异常?如果是后者,那我该怎么办?
我的直觉目前正在推动我使用BufferedStreams。这是正确的方法吗?
使用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。是的,我知道我还停留在古老的年代。
如果您想在文件被写入时读取它并只读取新内容,则可以使用以下方法实现。
要运行此程序,您需要从命令提示符/终端窗口启动它,并传递要读取的文件名。它会一直读取文件,除非您杀死该程序。
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();
}
}
BufferedReader
是如此无意义。如果它只被创建一次,则不需要跳过,因为缓冲读取器会在新行到达时读取它们。 - PiotrFileChannel
的此功能可能是一个起点。lock(long position, long size, boolean shared)
调用此方法将会阻塞,直到该区域可以被锁定。
它每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);
}
}
}
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,则可以使用文档结尾来发出信号。
如果使用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;
}
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();
}
虽然不是Java本身的问题,但您可能会遇到这样的情况:您已经将某些内容写入文件,但实际上它还没有被写入 - 它可能在某个缓存中,从同一文件读取可能并不能给您新的信息。
简短地说 - 使用flush()或其他相关系统调用来确保您的数据实际上已写入文件。
请注意,我不是在谈论操作系统级别的磁盘缓存 - 如果您的数据进入此处,则应在此点之后出现在read()中。 可能是语言本身缓存写入,等待缓冲区填满或文件刷新/关闭。
我从未尝试过,但你应该编写一个测试用例来查看在已经到达文件结尾后从流中读取是否可行,不管文件中是否有更多的数据。
你为什么不能使用管道输入/输出流?如果数据是从同一应用程序写入和读取的(如果是这样,你已经有了数据,为什么需要从文件中读取?)
否则,可能要一直读到文件结束,然后监视更改并定位到离开的位置继续... 但要注意竞争条件。