كيف يمكنني استخدام 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.نعم أعلم أنني لا أزال في العصر الحجري.

نصائح أخرى

إذا كنت تريد قراءة ملف أثناء كتابته وقراءة المحتوى الجديد فقط، فإن المتابعة ستساعدك على تحقيق ذلك.

لتشغيل هذا البرنامج، ستطلقه من نافذة موجه الأوامر/المحطة الطرفية وتمرير اسم الملف للقراءة.سوف يقرأ الملف إلا إذا قمت بقتل البرنامج.

جافا FileReader ج:\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();
    }
}

يمكنك أيضًا إلقاء نظرة على قناة جافا لقفل جزء من الملف.

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 سلسلة نصية إلى الملف ثم ينتظر حتى يضغط المستخدم على زر الإدخال قبل كتابة سطر آخر إلى الملف.الفكرة هي أنه يمكن تشغيله، ثم يمكن بدء القارئ لمعرفة كيفية تعامله مع الملف "الجزئي".القارئ الذي كتبته موجود في 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() أو أيًا كان استدعاء النظام ذي الصلة للتأكد من كتابة بياناتك فعليًا في الملف.

لاحظ أنني لا أتحدث عن ذاكرة التخزين المؤقت للقرص على مستوى نظام التشغيل - إذا دخلت بياناتك هنا، فيجب أن تظهر في قراءة () بعد هذه النقطة.من الممكن أن تقوم اللغة نفسها بالكتابة مؤقتًا، في انتظار امتلاء المخزن المؤقت أو مسح/إغلاق الملف.

هناك برنامج 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();
}

لم أجربه مطلقًا، ولكن يجب عليك كتابة حالة اختبار لمعرفة ما إذا كانت القراءة من الدفق بعد الوصول إلى النهاية ستعمل، بغض النظر عما إذا كان هناك المزيد من البيانات المكتوبة في الملف.

هل هناك سبب يمنعك من استخدام دفق الإدخال/الإخراج عبر الأنابيب؟هل تتم كتابة البيانات وقراءتها من نفس التطبيق (إذا كان الأمر كذلك، فأنت لديك البيانات، فلماذا تحتاج إلى القراءة من الملف)؟

بخلاف ذلك، يمكنك القراءة حتى نهاية الملف، ثم مراقبة التغييرات والبحث من حيث توقفت والمتابعة...على الرغم من الحذر من ظروف السباق.

لا يمكنك قراءة ملف تم فتحه من عملية أخرى باستخدام 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