Как использовать Java для чтения из файла, который активно записывается?

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

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

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

Ответ 1

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

Ответ 2

Посмотрите на Tailer от Apache Commons IO. Он обрабатывает большинство случаев краев.

Ответ 3

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

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

Эта функция FileChannel может быть запущена

lock(long position, long size, boolean shared) 

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

Ответ 4

Я полностью согласен с ответом Джошуа, 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

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

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

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

Ответ 6

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

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, конец документа можно было бы использовать для оповещения об этом.

Ответ 7

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

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

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

Ответ 8

Есть графический хвост Java с открытым исходным кодом, который делает это.

fooobar.com/questions/72089/...

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

Ответ 9

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

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

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

Ответ 10

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