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

时间:2008-08-06 23:57:10

标签: java file file-io

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

我认为可以这样做,但使用Java时涉及到的问题是什么?如果读取符合写入,它是否只是等待更多写入,直到文件关闭,或者读取是否会抛出异常?如果是后者,我该怎么办?

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

9 个答案:

答案 0 :(得分:35)

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

答案 1 :(得分:8)

如果您想在撰写文件时阅读文件并且只阅读新内容,那么以下内容将帮助您实现相同目标。

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

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();
    }
}

答案 2 :(得分:5)

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

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

FileChannel的这个功能可能是一个开始

lock(long position, long size, boolean shared) 

此方法的调用将阻止,直到可以锁定区域

答案 3 :(得分:5)

我完全同意Joshua's responseTailer适合这种情况下的工作。这是一个例子:

它在文件中每150毫秒写一行,同时每2500毫秒读取一个相同的文件

null

答案 4 :(得分:3)

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

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,则可以使用文档末尾发出信号。

答案 5 :(得分:1)

不是Java本身,但是你可能会遇到一些问题,你已经写了一些东西到文件,但它还没有实际写入 - 它可能在某个地方的缓存中,并且从同一个文件读取可能不会实际上给你新的信息。

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

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

答案 6 :(得分:1)

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

答案 7 :(得分:0)

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

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

否则,可能会读到文件末尾,然后监控更改并寻找您离开的地方并继续......但要注意竞争条件。

答案 8 :(得分:0)

您无法使用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;
}