Jak używać Javy do odczytu z pliku, do którego jest aktywnie zapisywany?

Mam aplikację, która zapisuje informacje do pliku. Informacje te są używane po wykonaniu w celu określenia przebiegu/awarii / poprawności aplikacji. Chciałbym móc odczytać plik tak, jak jest on zapisywany, aby móc wykonywać te testy pass/failure/correction w czasie rzeczywistym.

Zakładam, że jest to możliwe, ale na czym polega problem przy korzystaniu z Javy? Jeśli odczyt dogoni zapis, to czy będzie czekał na więcej zapisów aż plik zostanie zamknięty, czy czy odczyt spowoduje w tym momencie wyjątek? Jeśli to drugie, co mam wtedy zrobić?

Moja intuicja popycha mnie obecnie w kierunku buforowanych strumieni. To jest droga?

Author: Joris Schellekens, 2008-08-07

9 answers

Nie można uruchomić przykładu używając FileChannel.read(ByteBuffer), ponieważ nie jest to odczyt blokujący. Czy jednak kod poniżej działa:

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

Oczywiście to samo działałoby jako timer zamiast wątku, ale pozostawiam to programiście. Wciąż szukam lepszego sposobu, ale na razie mi to pasuje.

[[2]] Aha, a ja się tym zajmę: używam 1.4.2. Tak, Wiem, że jestem jeszcze w epoce kamienia.
 35
Author: Joseph Gordon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-21 15:42:13

Jeśli chcesz odczytać plik podczas jego pisania i tylko czytać nową zawartość, to podążanie za nim pomoże Ci osiągnąć to samo.

Aby uruchomić ten program, uruchom go z okna wiersza polecenia/terminala i podaj nazwę pliku do odczytu. Odczyta plik, chyba że zabijesz program.

Java FileReader c:\myfile.txt

Podczas wpisywania wiersza tekstu Zapisz go z notatnika, a zobaczysz tekst wydrukowany w konsoli.

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();
    }
}
 7
Author: tiger.spring,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-29 19:08:15

Możesz również spojrzeć na java channel, aby zablokować część pliku.

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

Ta funkcja FileChannel może być początkiem

lock(long position, long size, boolean shared) 

Wywołanie tej metody będzie blokowane do czasu zablokowania regionu

 5
Author: Frederic Morin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-01 16:31:53

Całkowicie się Zgadzam z odpowiedź Joshuy, Tailer nadaje się do pracy w tej sytuacji. Oto przykład:

Zapisuje linię co 150 ms w pliku, podczas czytania tego samego pliku co 2500 ms

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
Author: ToYonos,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:34:50

Odpowiedź brzmi "nie"... i "tak". Wydaje się, że nie ma realnego sposobu, aby dowiedzieć się, czy plik jest otwarty do zapisu przez inną aplikację. Tak więc czytanie z takiego pliku będzie trwało aż do wyczerpania zawartości. Posłuchałem Rady Mike ' a i napisałem jakiś kod testowy:

Pisarz.java zapisuje łańcuch znaków do pliku, a następnie czeka na naciśnięcie klawisza enter przed zapisaniem innej linii do pliku. Idea jest taka, że można go uruchomić, wówczas czytelnik może zacząć widzieć, jak radzi sobie z plik "częściowy". Czytelnik, który napisałem, jest w czytelniku.java.

Scenarzystajava

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

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

Nie gwarantuje, że ten kodeks jest najlepszą praktyką.

Pozostawia to sugerowaną przez Mike opcję okresowego sprawdzania, czy są nowe dane do odczytu z pliku. Wymaga to interwencji użytkownika w celu zamknięcia czytnika plików, gdy zostanie ustalone, że odczyt został zakończony. Lub czytelnikowi należy zapoznać się z zawartością pliku i być w stanie określić i zakończyć warunek zapisu. Jeśli zawartość była XML, koniec dokumentu może być użyty do zasygnalizowania tego.

 3
Author: Anthony Cramp,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-07 07:25:02

Nie Java per-se, ale możesz napotkać problemy, w których napisałeś coś do pliku, ale to jeszcze nie zostało napisane - może to być gdzieś w pamięci podręcznej, a Czytanie z tego samego pliku może nie dać ci nowych informacji.

Skrócona wersja-użyj flush () lub jakiegokolwiek innego odpowiedniego wywołania systemowego, aby upewnić się, że Twoje dane są rzeczywiście zapisane do pliku.

Uwaga nie mówię o pamięci podręcznej dysku na poziomie systemu operacyjnego - jeśli Twoje dane się tu dostają, powinny się pojawić w read () po tym punkcie. Może się zdarzyć, że sam język buforuje zapis, czekając, aż bufor się wypełni lub plik zostanie spłukany/zamknięty.

 1
Author: Matthew Schinckel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-07 00:31:47

Istnieje open Source Java Graficzny ogon, który to robi.

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
Author: Rodrigo Menezes,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:10:41

Nigdy tego nie próbowałem, ale powinieneś napisać przypadek testowy, aby sprawdzić, czy odczyt ze strumienia po naciśnięciu końca zadziała, niezależnie od tego, czy do pliku jest więcej danych.

Czy Jest jakiś powód, dla którego nie możesz używać strumienia wejścia/wyjścia? Czy dane są zapisywane i odczytywane z tej samej aplikacji (jeśli tak, to masz dane, dlaczego musisz czytać z pliku)?

W przeciwnym razie może czytać do końca pliku, a następnie monitorować zmiany i szukać miejsca, w którym przerwałeś i kontynuuj... ale uważaj na warunki wyścigowe.

 0
Author: Mike Stone,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-07 00:37:44

Nie można odczytać pliku, który jest otwierany z innego procesu za pomocą FileInputStream, FileReader lub RandomAccessFile.

Ale użycie FileChannel bezpośrednio zadziała:

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
Author: bebbo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-03 12:09:17