ฉันจะใช้ Java เพื่ออ่านจากไฟล์ที่กำลังเขียนอยู่ได้อย่างไร


99

ฉันมีแอปพลิเคชันที่เขียนข้อมูลลงในไฟล์ ข้อมูลนี้ใช้หลังการดำเนินการเพื่อตรวจสอบการส่งผ่าน / ล้มเหลว / ความถูกต้องของแอปพลิเคชัน ฉันต้องการที่จะอ่านไฟล์ในขณะที่กำลังเขียนเพื่อที่ฉันจะได้ตรวจสอบการผ่าน / ความล้มเหลว / ความถูกต้องเหล่านี้แบบเรียลไทม์

ฉันคิดว่ามันเป็นไปได้ที่จะทำสิ่งนี้ แต่ gotcha เกี่ยวข้องกับอะไรเมื่อใช้ Java? หากการอ่านตรงกับการเขียนจะรอให้มีการเขียนมากขึ้นจนกว่าไฟล์จะปิดหรือไม่หรือการอ่านจะทำให้เกิดข้อยกเว้น ณ จุดนี้? ถ้าเป็นอย่างหลังฉันจะทำอย่างไร?

สัญชาตญาณของฉันกำลังผลักดันฉันไปสู่ ​​BufferedStreams ทางนี้จะไปไหม


1
เดี๋ยวก่อนในขณะที่ฉันกำลังเผชิญกับสถานการณ์ที่คล้ายกันฉันกำลังพูดว่าคุณพบวิธีแก้ปัญหาที่ดีกว่าที่ยอมรับหรือไม่?
Asaf David

ฉันรู้ว่านี่เป็นคำถามเก่า แต่เพื่อประโยชน์ของผู้อ่านในอนาคตคุณสามารถขยายกรณีการใช้งานของคุณอีกเล็กน้อยได้หรือไม่ หากไม่มีข้อมูลเพิ่มเติมก็อาจสงสัยว่าคุณกำลังแก้ปัญหาผิดหรือไม่
user359996

ดูการใช้Tailerจาก Apache Commons IO จัดการกับขอบเคสส่วนใหญ่
Joshua

2
ใช้ฐานข้อมูล สถานการณ์ 'อ่านไฟล์ในขณะที่กำลังเขียน' เหล่านี้จบลงด้วยน้ำตา
Marquis of Lorne

@EJP - คุณแนะนำ DB ไหน ฉันเดาว่า MySQL เป็นการเริ่มต้นที่ดีหรือไม่?
กาแฟ

คำตอบ:


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 - คุณจะต้องย้ายไปที่ Drone ทุกวันในวันนี้
TungstenX

12

หากคุณต้องการอ่านไฟล์ในขณะที่กำลังเขียนและอ่านเฉพาะเนื้อหาใหม่การทำตามจะช่วยให้คุณได้สิ่งเดียวกัน

ในการเรียกใช้โปรแกรมนี้คุณจะเปิดใช้งานจากหน้าต่าง command prompt / terminal และส่งชื่อไฟล์เพื่ออ่าน มันจะอ่านไฟล์เว้นแต่คุณจะฆ่าโปรแกรม

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

2
มีความเป็นไปได้หรือไม่ที่ระหว่างเวลาที่อ่านไฟล์และเวลาที่ใช้ความยาวไฟล์กระบวนการภายนอกอาจเพิ่มข้อมูลเพิ่มเติมให้กับไฟล์ได้ หากเป็นเช่นนั้นจะส่งผลให้กระบวนการอ่านขาดข้อมูลที่เขียนลงในไฟล์
JohnC

1
รหัสนี้ใช้ CPU มากเนื่องจากลูปไม่มีการเรียก thread.sleep การไม่เพิ่มความล่าช้าเล็กน้อยรหัสนี้มีแนวโน้มที่จะทำให้ CPU ยุ่งมาก
ChaitanyaBhatt

ความคิดเห็นทั้งสองด้านบนเป็นความจริงและนอกจากนี้BufferedReaderการสร้างนี้ในการเรียกลูปแต่ละครั้งก็ไม่มีจุดหมาย ด้วยการสร้างขึ้นเมื่อการข้ามไม่จำเป็นเนื่องจากผู้อ่านบัฟเฟอร์จะอ่านบรรทัดใหม่เมื่อมาถึง
Piotr

5

คุณอาจดูที่ช่อง java เพื่อล็อคส่วนหนึ่งของไฟล์

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

ฟังก์ชันนี้FileChannelอาจเป็นการเริ่มต้น

lock(long position, long size, boolean shared) 

การเรียกใช้วิธีนี้จะปิดกั้นจนกว่าจะสามารถล็อกพื้นที่ได้


5

ผมเห็นด้วยกับการตอบสนองของโจชัว , Tailerเหมาะสำหรับงานในสถานการณ์เช่นนี้ นี่คือตัวอย่าง:

เขียนบรรทัดทุกๆ 150 ms ในไฟล์ในขณะที่อ่านไฟล์เดียวกันนี้ทุกๆ 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);
        }
    }
}

ลิงก์ของคุณไปยัง Tailer เสีย
ชิงทรัพย์รับบี

2
@StealthRabbi สิ่งที่ดีที่สุดที่ต้องทำคือค้นหาลิงก์ที่ถูกต้องและแก้ไขคำตอบด้วย
igracia

3

คำตอบน่าจะเป็น "ไม่" ... และ "ใช่" ดูเหมือนว่าจะไม่มีทางรู้ได้อย่างแท้จริงว่าไฟล์นั้นเปิดให้เขียนโดยแอปพลิเคชันอื่นหรือไม่ ดังนั้นการอ่านจากไฟล์ดังกล่าวจะดำเนินต่อไปจนกว่าเนื้อหาจะหมด ฉันรับคำแนะนำของไมค์และเขียนรหัสทดสอบ:

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 per-se แต่คุณอาจพบปัญหาที่คุณเขียนบางอย่างลงในไฟล์ แต่ยังไม่ได้เขียนจริง - อาจอยู่ในแคชที่ไหนสักแห่งและการอ่านจากไฟล์เดียวกันอาจไม่ได้ให้คุณ ข้อมูลใหม่

เวอร์ชันสั้น - ใช้ flush () หรือการเรียกระบบที่เกี่ยวข้องเพื่อให้แน่ใจว่าข้อมูลของคุณถูกเขียนลงในไฟล์จริง

หมายเหตุฉันไม่ได้พูดถึงแคชดิสก์ระดับ OS - หากข้อมูลของคุณเข้ามาที่นี่ข้อมูลนั้นควรปรากฏใน read () หลังจากจุดนี้ อาจเป็นไปได้ว่าภาษานั้นเขียนแคชไว้รอจนกว่าบัฟเฟอร์จะเต็มหรือไฟล์ถูกล้าง / ปิด


1

มี Open Source 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

ฉันไม่เคยลอง แต่คุณควรเขียนกรณีทดสอบเพื่อดูว่าการอ่านจากสตรีมหลังจากที่คุณกดจุดสิ้นสุดจะใช้งานได้หรือไม่ไม่ว่าจะมีข้อมูลที่เขียนลงในไฟล์มากขึ้นหรือไม่ก็ตาม

มีเหตุผลที่คุณไม่สามารถใช้อินพุต / เอาต์พุตสตรีมแบบ piped ได้หรือไม่? ข้อมูลถูกเขียนและอ่านจากแอปพลิเคชันเดียวกันหรือไม่ (ถ้าเป็นเช่นนั้นคุณมีข้อมูลเหตุใดคุณจึงต้องอ่านจากไฟล์)

มิฉะนั้นอาจอ่านจนจบไฟล์จากนั้นตรวจสอบการเปลี่ยนแปลงและค้นหาจุดที่คุณค้างไว้และดำเนินการต่อ ... แม้ว่าจะระวังสภาพการแข่งขัน

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.