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

StackOverflow https://stackoverflow.com/questions/4149

  •  08-06-2019
  •  | 
  •  

Вопрос

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

Я предполагаю, что это возможно сделать, но какие проблемы возникают при использовании 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();
    }
}

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

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

Эта функция FileChannel это могло бы стать началом

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

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

Writer.java записывает строку в file, а затем ждет, пока пользователь нажмет клавишу enter, прежде чем записать другую строку в file.Идея заключается в том, что его можно запустить, после чего можно запустить программу чтения, чтобы увидеть, как она справляется с "частичным" файлом.Ридер, о котором я написал, находится в 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, для обозначения этого можно было бы использовать конец документа.

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

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

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

Существует графический интерфейс 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();
}

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

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

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

Вы не можете прочитать файл, открытый из другого процесса с помощью 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;
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top