Pergunta

Eu tenho um aplicativo que grava informações em um arquivo.Essas informações são usadas após a execução para determinar a aprovação/falha/correção do aplicativo.Gostaria de poder ler o arquivo enquanto ele está sendo gravado para poder fazer essas verificações de aprovação/falha/correção em tempo real.

Presumo que seja possível fazer isso, mas quais são os problemas envolvidos ao usar Java?Se a leitura alcançar a escrita, ela apenas esperará por mais gravações até que o arquivo seja fechado ou a leitura lançará uma exceção neste ponto?Neste último caso, o que eu faço então?

Minha intuição está atualmente me empurrando para o BufferedStreams.É este o caminho a percorrer?

Foi útil?

Solução

Não foi possível fazer o exemplo funcionar usando FileChannel.read(ByteBuffer) porque não é uma leitura de bloqueio.No entanto, fez com que o código abaixo funcionasse:

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

É claro que a mesma coisa funcionaria como um temporizador em vez de um thread, mas deixo isso para o programador.Ainda estou procurando uma maneira melhor, mas isso funciona para mim por enquanto.

Ah, e vou fazer uma advertência com:Estou usando 1.4.2.Sim, eu sei que ainda estou na idade da pedra.

Outras dicas

Se você deseja ler um arquivo enquanto ele está sendo gravado e apenas ler o novo conteúdo, o seguinte o ajudará a conseguir o mesmo.

Para executar este programa você irá iniciá-lo a partir do prompt de comando/janela do terminal e passar o nome do arquivo para leitura.Ele lerá o arquivo, a menos que você elimine o programa.

java FileReader c:\meuarquivo.txt

Ao digitar uma linha de texto, salve-a no bloco de notas e você verá o texto impresso no 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();
    }
}

Você também pode dar uma olhada no canal java para bloquear uma parte de um arquivo.

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

Esta função do FileChannel pode ser um começo

lock(long position, long size, boolean shared) 

Uma invocação deste método será bloqueada até que a região possa ser bloqueada

concordo totalmente com A resposta de Josué, Alfaiate está apto para o trabalho nesta situação.Aqui está um exemplo :

Ele escreve uma linha a cada 150 ms em um arquivo, enquanto lê esse mesmo arquivo a cada 2.500 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);
        }
    }
}

A resposta parece ser "não"...e sim".Parece não haver uma maneira real de saber se um arquivo está aberto para gravação por outro aplicativo.Portanto, a leitura desse arquivo irá apenas progredir até que o conteúdo se esgote.Segui o conselho de Mike e escrevi alguns códigos de teste:

Writer.java grava uma string no arquivo e espera que o usuário pressione Enter antes de escrever outra linha no arquivo.A ideia é que ele possa ser inicializado, então o leitor poderá começar a ver como ele lida com o arquivo "parcial".O leitor que escrevi está em Reader.java.

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

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

Não há garantias de que este código seja uma prática recomendada.

Isso deixa a opção sugerida por Mike de verificar periodicamente se há novos dados para serem lidos no arquivo.Isso requer então a intervenção do usuário para fechar o leitor de arquivos quando for determinado que a leitura foi concluída.Ou o leitor precisa estar ciente do conteúdo do arquivo e ser capaz de determinar a condição de final de gravação.Se o conteúdo fosse XML, o final do documento poderia ser usado para sinalizar isso.

Não é Java em si, mas você pode se deparar com problemas em que escreveu algo em um arquivo, mas ainda não foi escrito - pode estar em um cache em algum lugar e a leitura do mesmo arquivo pode não fornecer a você as novas informações.

Versão curta - use flush() ou qualquer que seja a chamada de sistema relevante para garantir que seus dados sejam realmente gravados no arquivo.

Observe que não estou falando sobre o cache de disco no nível do sistema operacional - se seus dados entrarem aqui, eles deverão aparecer em read() após este ponto.Pode ser que a própria linguagem armazene em cache as gravações, aguardando até que um buffer seja preenchido ou o arquivo seja liberado/fechado.

Existe um Java Graphic Tail de código aberto que faz isso.

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

Eu nunca tentei, mas você deve escrever um caso de teste para ver se a leitura de um fluxo depois de chegar ao final funcionará, independentemente de haver mais dados gravados no arquivo.

Existe um motivo pelo qual você não pode usar um fluxo de entrada/saída canalizado?Os dados estão sendo gravados e lidos no mesmo aplicativo (se sim, você tem os dados, por que precisa ler o arquivo)?

Caso contrário, talvez leia até o final do arquivo, monitore as alterações e procure onde parou e continue ...mas esteja atento às condições da corrida.

Você não pode ler um arquivo aberto por outro processo usando FileInputStream, FileReader ou RandomAccessFile.

Mas usar o FileChannel diretamente funcionará:

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;
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top