Aktif olarak yazılmakta olan bir dosyadan okumak için Java'yı nasıl kullanırım?


99

Dosyaya bilgi yazan bir uygulamam var. Bu bilgi, uygulamanın başarılı / başarısız / doğruluğunu belirlemek için yürütme sonrası kullanılır. Bu başarılı / başarısız / doğruluk kontrollerini gerçek zamanlı olarak yapabilmem için dosyayı yazılırken okuyabilmek istiyorum.

Bunu yapmanın mümkün olduğunu varsayıyorum, ancak Java'yı kullanırken ne işe yarar? Okuma, yazıyı yakalarsa, dosya kapanana kadar daha fazla yazmayı mı bekler yoksa okuma bu noktada bir istisna atar mı? İkincisi ise, o zaman ne yapacağım?

Sezgim şu anda beni BufferedStreams'e doğru itiyor. Gitmenin yolu bu mu?


1
Hey, benzer bir senaryo ile karşı karşıya olduğum için, kabul edilenden daha iyi bir çözüm bulup bulmadığınızı söylüyordum.
Asaf David

Bunun eski bir soru olduğunu biliyorum, ancak gelecekteki okuyucuların iyiliği için kullanım durumunuzu biraz daha genişletebilir misiniz? Daha fazla bilgiye sahip olmadan, belki de yanlış sorunu çözüp çözmediğiniz merak edilir.
user359996

Apache Commons IO'dan Tailer'ı kullanmayı inceleyin . Uç vakaların çoğunu ele alıyor.
Joshua

2
Bir veritabanı kullanın. Bu 'bir dosyayı yazılırken oku' senaryoları gözyaşlarıyla sonuçlanır.
Lorne Markisi

@EJP - Hangi DB'yi önerirsiniz? MySQL'in iyi bir başlangıç ​​olduğunu tahmin ediyorum?
Coffee

Yanıtlar:


39

FileChannel.read(ByteBuffer)Okumayı engellemediği için örnek kullanılarak çalışılamadı . Ancak çalışmak için aşağıdaki kodu aldınız mı:

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

Tabii ki aynı şey bir iş parçacığı yerine bir zamanlayıcı olarak da işe yarar, ama bunu programcıya bırakıyorum. Hâlâ daha iyi bir yol arıyorum ama bu şimdilik benim için çalışıyor.

Oh, ve bunu şununla uyaracağım: 1.4.2 kullanıyorum. Evet biliyorum hala taş çağındayım.


1
Bunu eklediğin için teşekkürler ... hiç yapamadığım bir şey. Bence Blade'in dosyayı kilitleme cevabı da iyi bir cevap. Ancak Java 6 gerektirir (sanırım).
Anthony Cramp

@JosephGordon - Bu günlerden birinde Drone çağına taşınmanız gerekecek ;-)
TungstenX

12

Bir dosyayı yazılırken okumak ve yalnızca yeni içeriği okumak istiyorsanız, aşağıdaki aynı şeyi elde etmenize yardımcı olacaktır.

Bu programı çalıştırmak için, komut isteminden / terminal penceresinden başlatacak ve okunacak dosya adını ileteceksiniz. Programı sonlandırmadığınız sürece dosyayı okuyacaktır.

java Dosya Okuyucusu c: \ dosyam.txt

Bir satır metni yazarken not defterinden kaydedin ve konsolda yazdırılan metni göreceksiniz.

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
Dosyanın okunduğu ve dosya uzunluğunun alındığı zaman arasında, harici işlemin dosyaya daha fazla veri ekleyebilme olasılığı yok mu? Öyleyse, bu dosyaya yazılan verileri eksik okuma işlemiyle sonuçlanır.
JohnC

1
Döngü içinde bir thread.sleep çağrısı bulunmadığından bu kod çok fazla CPU tüketir. Az miktarda gecikme eklemeden bu kod CPU'yu çok meşgul tutma eğilimindedir.
ChaitanyaBhatt

Yukarıdaki her iki yorum da doğrudur ve ayrıca BufferedReaderher döngü çağrısındaki bu oluşturma çok anlamsızdır. Oluşturulduğunda, tamponlu okuyucu geldiklerinde yeni satırları okuyacağından, atlama gerekli olmayacaktır.
Piotr


5

Joshua'nın cevabına tamamen katılıyorum , Tailer bu durumda işe uygun. İşte bir örnek :

Bir dosyaya her 150 ms'de bir satır yazar, aynı dosyayı her 2500 ms'de bir okurken

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 ile bağlantınız koptu.
Stealth Rabbi

2
@StealthRabbi O halde yapılacak en iyi şey, doğru bağlantıyı aramak ve bununla yanıtı düzenlemektir.
igracia

3

Cevap "hayır" ... ve "evet" gibi görünüyor. Bir dosyanın başka bir uygulama tarafından yazmaya açık olup olmadığını bilmenin gerçek bir yolu yok gibi görünüyor. Dolayısıyla, böyle bir dosyadan okumak, içerik tükenene kadar ilerleyecektir. Mike'ın tavsiyesine uydum ve bir test kodu yazdım:

Writer.java dosyaya bir dizge yazar ve ardından dosyaya başka bir satır yazmadan önce kullanıcının enter tuşuna basmasını bekler. Bunun başlatılabileceği fikri, daha sonra "kısmi" dosya ile nasıl başa çıktığını görmek için bir okuyucu başlatılabilir. Yazdığım okuyucu Reader.java'da.

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

Bu kodun en iyi uygulama olduğunu garanti etmez.

Bu, Mike tarafından önerilen, dosyadan okunacak yeni veri olup olmadığını düzenli olarak kontrol etme seçeneğini bırakır. Bu, daha sonra, okumanın tamamlandığı belirlendiğinde dosya okuyucuyu kapatmak için kullanıcı müdahalesini gerektirir. Ya da okuyucunun dosyanın içeriğinden haberdar edilmesi ve yazma koşulunu belirleyip bitirebilmesi gerekir. İçerik XML ise, belgenin sonu bunu belirtmek için kullanılabilir.


1

Kendi başına Java değil, ancak bir dosyaya bir şeyler yazdığınız, ancak henüz yazılmadığı sorunlarla karşılaşabilirsiniz - bir yerde bir önbellekte olabilir ve aynı dosyadan okumak aslında size vermeyebilir yeni bilgiler.

Kısa versiyon - verilerinizin gerçekten dosyaya yazıldığından emin olmak için flush () veya ilgili sistem çağrısı ne olursa olsun kullanın.

Not İşletim sistemi seviyesi disk önbelleğinden bahsetmiyorum - verileriniz buraya girerse, bu noktadan sonra bir read () içinde görünmelidir. Dilin kendisinin yazmayı önbelleğe alması, bir arabellek dolana kadar veya dosyanın boşaltılması / kapatılması olabilir.


1

Bunu yapan bir Açık Kaynak Java Grafik Kuyruğu vardır.

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 veya RandomAccessFile kullanılarak başka bir işlemden açılan bir dosyayı okuyamazsınız.

Ancak doğrudan FileChannel kullanmak işe yarayacaktır:

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

Hiç denemedim, ancak dosyaya yazılan daha fazla veri olup olmadığına bakılmaksızın, sonuna ulaştıktan sonra bir akıştan okumanın işe yarayıp yaramayacağını görmek için bir test durumu yazmalısınız.

Borulu bir giriş / çıkış akışı kullanamamanızın bir nedeni var mı? Veriler aynı uygulamadan yazılıyor ve okunuyor mu (eğer öyleyse, verilere sahipsiniz, neden dosyadan okumanız gerekiyor)?

Aksi takdirde, belki dosyanın sonuna kadar okuyun, sonra değişiklikleri izleyin ve kaldığınız yere gidin ve devam edin ... yine de yarış koşullarına dikkat edin.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.