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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4149/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-10 23:32:09  来源:igfitidea点击:

How do I use Java to read from a file that is actively being written to?

javafilefile-io

提问by Anthony Cramp

I have an application that writes information to file. This information is used post-execution to determine pass/failure/correctness of the application. I'd like to be able to read the file as it is being written so that I can do these pass/failure/correctness checks in real time.

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

I assume it is possible to do this, but what are the gotcha's involved when using Java? If the reading catches up to the writing, will it just wait for more writes up until the file is closed, or will the read throw an exception at this point? If the latter, what do I do then?

我认为这样做是可能的,但是在使用 Java 时涉及哪些问题?如果读取赶上写入,它会等待更多写入直到文件关闭,还是此时读取会抛出异常?如果是后者,那我该怎么办?

My intuition is currently pushing me towards BufferedStreams. Is this the way to go?

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

采纳答案by Joseph Gordon

Could not get the example to work using FileChannel.read(ByteBuffer)because it isn't a blocking read. Did however get the code below to work:

无法使用该示例,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;
            }
        }
    }
}

Of course the same thing would work as a timer instead of a thread, but I leave that up to the programmer. I'm still looking for a better way, but this works for me for now.

当然,同样的事情可以作为计时器而不是线程工作,但我把它留给程序员。我仍在寻找更好的方法,但这现在对我有用。

Oh, and I'll caveat this with: I'm using 1.4.2. Yes I know I'm in the stone ages still.

哦,我要说明一下:我使用的是 1.4.2。是的,我知道我仍然处于石器时代。

回答by Matthew Schinckel

Not Java per-se, but you may run into issues where you have written something to a file, but it hasn't been actually written yet - it might be in a cache somewhere, and reading from the same file may not actually give you the new information.

不是 Java 本身,但您可能会遇到一些问题,即您已将某些内容写入文件,但尚未实际写入 - 它可能位于某处的缓存中,而从同一文件中读取实际上可能不会给您新信息。

Short version - use flush() or whatever the relevant system call is to ensure that your data is actually written to the file.

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

Note I am not talking about the OS level disk cache - if your data gets into here, it should appear in a read() after this point. It may be that the language itself caches writes, waiting until a buffer fills up or file is flushed/closed.

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

回答by Mike Stone

I've never tried it, but you should write a test case to see if reading from a stream after you have hit the end will work, regardless of if there is more data written to the file.

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

Is there a reason you can't use a piped input/output stream? Is the data being written and read from the same application (if so, you have the data, why do you need to read from the file)?

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

Otherwise, maybe read till end of file, then monitor for changes and seek to where you left off and continue... though watch out for race conditions.

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

回答by Anthony Cramp

The answer seems to be "no" ... and "yes". There seems to be no real way to know if a file is open for writing by another application. So, reading from such a file will just progress until content is exhausted. I took Mike's advice and wrote some test code:

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

Writer.java writes a string to file and then waits for the user to hit enter before writing another line to file. The idea being that it could be started up, then a reader can be started to see how it copes with the "partial" file. The reader I wrote is in Reader.java.

Writer.java 将一个字符串写入文件,然后等待用户在将另一行写入文件之前按回车键。这个想法是可以启动它,然后可以启动阅读器来查看它如何处理“部分”文件。我写的阅读器在 Reader.java 中。

Writer.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

阅读器.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);
    }
}

No guarantees that this code is best practice.

不保证此代码是最佳实践。

This leaves the option suggested by Mike of periodically checking if there is new data to be read from the file. This then requires user intervention to close the file reader when it is determined that the reading is completed. Or, the reader needs to be made aware the content of the file and be able to determine and end of write condition. If the content were XML, the end of document could be used to signal this.

这留下了 Mike 建议的选项,即定期检查是否有要从文件中读取的新数据。这然后需要用户干预以在确定读取完成时关闭文件读取器。或者,读者需要知道文件的内容并能够确定和结束写入条件。如果内容是 XML,则可以使用文档结尾来表示这一点。

回答by Frederic Morin

You might also take a look at java channel for locking a part of a file.

您还可以查看用于锁定文件的一部分的 java 通道。

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

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

This function of the FileChannelmight be a start

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

lock(long position, long size, boolean shared) 

An invocation of this method will block until the region can be locked

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

回答by Rodrigo Menezes

There are a Open Source Java Graphic Tail that does this.

有一个开源 Java Graphic Tail 可以做到这一点。

https://stackoverflow.com/a/559146/1255493

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

回答by ToYonos

I totally agree with Joshua's response, Taileris fit for the job in this situation. Here is an example :

我完全同意Joshua 的回答Tailer在这种情况下适合这份工作。这是一个例子:

It writes a line every 150 ms in a file, while reading this very same file every 2500 ms

它每 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);
        }
    }
}

回答by tiger.spring

If you want to read a file while it is being written and only read the new content then following will help you achieve the same.

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

To run this program you will launch it from command prompt/terminal window and pass the file name to read. It will read the file unless you kill the program.

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

java FileReader c:\myfile.txt

java FileReader c:\myfile.txt

As you type a line of text save it from notepad and you will see the text printed in the console.

当您键入一行文本时,将其从记事本中保存,您将在控制台中看到打印的文本。

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

回答by bebbo

You can't read a file which is opened from another process using FileInputStream, FileReader or RandomAccessFile.

您无法读取使用 FileInputStream、FileReader 或 RandomAccessFile 从另一个进程打开的文件。

But using FileChannel directly will work:

但是直接使用 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;
}