Как использовать Java для чтения из файла, в который активно производится запись? - PullRequest
95 голосов
/ 07 августа 2008

У меня есть приложение, которое записывает информацию в файл. Эта информация используется после выполнения для определения прохождения / отказа / правильности приложения. Я хотел бы иметь возможность читать файл в том виде, в котором он записывается, чтобы я мог выполнять эти проверки на наличие ошибок / ошибок / правильности в реальном времени.

Я предполагаю, что это можно сделать, но в чем заключается проблема при использовании Java? Если чтение совпадает с записью, будет ли оно ждать новых записей до тех пор, пока файл не будет закрыт, или чтение вызовет исключение на этом этапе? Если последнее, что мне тогда делать?

Моя интуиция в настоящее время подталкивает меня к BufferedStreams. Это путь?

Ответы [ 9 ]

35 голосов
/ 30 сентября 2008

Не удалось заставить пример работать с использованием 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. Да, я знаю, что все еще в каменном веке.

8 голосов
/ 29 сентября 2015

Если вы хотите прочитать файл во время его записи и только читать новый контент, то следующее поможет вам достичь того же.

Для запуска этой программы вы запустите ее из командной строки / окна терминала и передадите имя файла для чтения. Он будет читать файл, если вы не убьете программу.

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();
    }
}
5 голосов
/ 25 сентября 2015

Я полностью согласен с ответом Джошуа , Tailer подходит для работы в этой ситуации Вот пример:

Он записывает строку в файл каждые 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);
        }
    }
}
5 голосов
/ 01 сентября 2008

Вы также можете взглянуть на канал Java для блокировки части файла.

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

Эта функция FileChannel может быть началом

lock(long position, long size, boolean shared) 

Вызов этого метода будет блокироваться до тех пор, пока регион не будет заблокирован

3 голосов
/ 07 августа 2008

Ответом будет «нет» ... и «да». Кажется, нет никакого реального способа узнать, открыт ли файл для записи другим приложением. Таким образом, чтение из такого файла будет продолжаться до тех пор, пока содержимое не будет исчерпано. Я воспользовался советом Майка и написал тестовый код:

Writer.java записывает строку в файл, а затем ждет, пока пользователь нажмет клавишу ввода, прежде чем записать другую строку в файл. Идея в том, что он может быть запущен, а затем можно запустить читатель, чтобы увидеть, как он справляется с «частичным» файлом. Читатель, который я написал, находится в 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);
    }
}

Нет гарантий, что этот код является наилучшей практикой.

Это оставляет вариант, предложенный Майком, периодически проверять, есть ли новые данные для чтения из файла. Затем требуется вмешательство пользователя для закрытия программы чтения файлов, когда определено, что чтение завершено. Или читатель должен быть осведомлен о содержимом файла и уметь определять и конец условия записи. Если бы содержимое было XML, конец документа мог бы использоваться для обозначения этого.

1 голос
/ 02 марта 2015

Существует графический код 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();
}
1 голос
/ 07 августа 2008

Не Java как таковая, но вы можете столкнуться с проблемами, когда вы что-то записали в файл, но на самом деле это еще не записано - это может быть где-то в кеше, и чтение из того же файла может не на самом деле дать вам новую информацию.

Короткая версия - используйте flush () или любой другой системный вызов, чтобы убедиться, что ваши данные действительно записаны в файл.

Примечание. Я не говорю о дисковом кеше на уровне ОС - если ваши данные попадают сюда, они должны появиться в read () после этой точки. Может случиться так, что сам язык кеширует записи, ожидая, пока буфер не заполнится или файл не будет очищен / закрыт.

0 голосов
/ 03 января 2016

Вы не можете прочитать файл, открытый другим процессом, используя 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;
}
0 голосов
/ 07 августа 2008

Я никогда не пробовал, но вы должны написать тестовый пример, чтобы увидеть, будет ли работать чтение из потока после того, как вы дойдете до конца, независимо от того, есть ли в файл больше данных.

Есть ли причина, по которой вы не можете использовать потоковый поток ввода / вывода? Данные записываются и читаются из одного и того же приложения (если это так, у вас есть данные, зачем вам нужно читать из файла)?

В противном случае, возможно, прочитайте до конца файла, затем следите за изменениями и ищите, где вы остановились, и продолжайте ... хотя следите за условиями гонки.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...