현재 기록중인 파일에서 Java를 사용하여 읽으려면 어떻게해야합니까?


99

파일에 정보를 쓰는 응용 프로그램이 있습니다. 이 정보는 애플리케이션의 합격 / 불합격 / 정확성을 결정하기 위해 실행 후 사용됩니다. 실시간으로 이러한 합격 / 불합격 / 정확성 검사를 수행 할 수 있도록 작성중인 파일을 읽을 수 있기를 바랍니다.

나는 이것을 할 수 있다고 가정하지만 Java를 사용할 때 어떤 문제가 있습니까? 읽기가 쓰기를 따라 잡으면 파일이 닫힐 때까지 더 많은 쓰기를 기다릴까요, 아니면이 시점에서 읽기가 예외를 던질까요? 후자의 경우 어떻게해야합니까?

내 직감은 현재 나를 BufferedStreams로 밀고 있습니다. 이게 갈 길 이니?


1
이봐, 내가 비슷한 시나리오에 직면하고 있기 때문에 받아 들여진 것보다 더 나은 해결책을 찾았다면 나는 말하고 있었다.
Asaf David

나는 이것이 오래된 질문이라는 것을 알고 있지만 미래의 독자를 위해 사용 사례를 조금 더 확장 할 수 있습니까? 더 많은 정보가 없으면 잘못된 문제를 해결하고 있는지 궁금해합니다.
user359996

Apache Commons IO 의 Tailer 사용을 살펴보세요 . 대부분의 엣지 케이스를 처리합니다.
Joshua

2
데이터베이스를 사용하십시오. 이러한 '파일을 쓰는 동안 읽기'시나리오는 눈물로 끝납니다.
Marquis of Lorne

@EJP-어떤 DB를 추천합니까? MySQL이 좋은 시작이라고 생각하고 있습니까?
Coffee

답변:


39

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를 사용하고 있습니다. 예, 아직 석기 시대에 있다는 것을 압니다.


1
이것을 추가 해주셔서 감사합니다. 파일 잠금에 대한 Blade의 대답도 좋은 것 같습니다. 그러나 Java 6이 필요합니다.
Anthony Cramp

@JosephGordon - 당신은 드론 세 ;-) 요즘으로 이동해야합니다
TungstenX

12

작성하는 동안 파일을 읽고 새 내용 만 읽으려면 다음을 수행하면 동일한 결과를 얻을 수 있습니다.

이 프로그램을 실행하려면 명령 프롬프트 / 터미널 창에서 실행하고 읽을 파일 이름을 전달합니다. 프로그램을 종료하지 않으면 파일을 읽습니다.

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

2
파일을 읽는 시간과 파일 길이가 걸리는 시간 사이에 외부 프로세스가 파일에 더 많은 데이터를 추가 할 가능성이 있습니까? 그렇다면 읽기 프로세스에서 파일에 기록 된 데이터가 누락 될 수 있습니다.
JohnC

1
이 코드는 루프에 thread.sleep 호출이 없기 때문에 많은 CPU를 소비합니다. 약간의 지연을 추가하지 않으면이 코드는 CPU를 매우 바쁘게 유지하는 경향이 있습니다.
ChaitanyaBhatt

위의 두 설명은 모두 사실이며 추가 BufferedReader로 각 루프 호출 에서이 생성은 무의미합니다. 버퍼링 된 리더가 새 줄이 도착할 때 읽을 수 있기 때문에 한 번 생성되면 건너 뛰기가 필요하지 않습니다.
Piotr


5

나는 Joshua의 반응에 전적으로 동의합니다 . Tailer 는이 상황에서 일에 적합합니다. 다음은 예입니다.

파일에 150ms마다 한 줄을 쓰고 2500ms마다 동일한 파일을 읽습니다.

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

Tailer에 대한 링크가 끊어졌습니다.
Stealth Rabbi

2
@StealthRabbi 가장 좋은 방법은 올바른 링크를 검색하여 답변을 편집하는 것입니다.
igracia

3

대답은 "아니오"... 그리고 "예"인 것 같습니다. 다른 응용 프로그램에서 쓰기 위해 파일이 열려 있는지 알 수있는 실제 방법이없는 것 같습니다. 따라서 이러한 파일에서 읽는 것은 내용이 모두 소진 될 때까지 진행됩니다. 나는 Mike의 조언을 듣고 몇 가지 테스트 코드를 작성했습니다.

Writer.java는 파일에 문자열을 쓴 다음 파일에 다른 줄을 쓰기 전에 사용자가 Enter 키를 누를 때까지 기다립니다. 시작될 수 있다는 생각은 독자가 "부분"파일에 어떻게 대처하는지보기 시작하는 것입니다. 내가 쓴 독자는 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);
    }
}

이 코드가 모범 사례라는 보장은 없습니다.

그러면 파일에서 읽을 새 데이터가 있는지 주기적으로 확인하는 Mike가 제안한 옵션이 남습니다. 그러면 읽기가 완료되었다고 판단되면 파일 판독기를 닫으려면 사용자 개입이 필요합니다. 또는 독자가 파일의 내용을 인식하고 쓰기 조건을 결정하고 종료 할 수 있어야합니다. 내용이 XML 인 경우 문서의 끝을 사용하여이를 알릴 수 있습니다.


1

Java는 아니지만 파일에 무언가를 작성했지만 아직 실제로 작성되지 않은 문제가 발생할 수 있습니다. 캐시 어딘가에있을 수 있으며 동일한 파일에서 읽는 것이 실제로 제공되지 않을 수 있습니다. 새로운 정보.

짧은 버전-flush () 또는 관련 시스템 호출을 사용하여 데이터가 실제로 파일에 기록되도록합니다.

참고 저는 OS 레벨 디스크 캐시에 대해 말하는 것이 아닙니다. 데이터가 여기에 들어 오면이 시점 이후에 read ()에 나타나야합니다. 언어 자체가 쓰기를 캐시하고 버퍼가 가득 차거나 파일이 플러시 / 닫힐 때까지 기다릴 수 있습니다.


1

이를 수행하는 오픈 소스 Java Graphic Tail이 있습니다.

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

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

나는 그것을 시도한 적이 없지만 파일에 더 많은 데이터가 기록되었는지 여부에 관계없이 끝을 낸 후 스트림에서 읽는 것이 작동하는지 확인하기 위해 테스트 케이스를 작성해야합니다.

파이프 된 입력 / 출력 스트림을 사용할 수없는 이유가 있습니까? 동일한 응용 프로그램에서 데이터를 쓰고 읽습니까 (그렇다면 데이터가있는 경우 파일에서 읽어야하는 이유)?

그렇지 않으면 파일 끝까지 읽은 다음 변경 사항을 모니터링하고 중단 한 부분을 찾아 계속해서 경쟁 조건을 조심하십시오.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.