Comment utiliser Java pour lire un fichier dans lequel une écriture est active ?

StackOverflow https://stackoverflow.com/questions/4149

  •  08-06-2019
  •  | 
  •  

Question

J'ai une application qui écrit des informations dans un fichier.Ces informations sont utilisées après l'exécution pour déterminer la réussite/l'échec/l'exactitude de l'application.J'aimerais pouvoir lire le fichier au fur et à mesure de son écriture afin de pouvoir effectuer ces vérifications de réussite/échec/exactitude en temps réel.

Je suppose qu'il est possible de le faire, mais quels sont les pièges impliqués lors de l'utilisation de Java ?Si la lecture rattrape l'écriture, attendra-t-elle simplement d'autres écritures jusqu'à ce que le fichier soit fermé, ou la lecture lèvera-t-elle une exception à ce stade ?Dans ce dernier cas, que dois-je faire alors ?

Mon intuition me pousse actuellement vers BufferedStreams.Est-ce la voie à suivre ?

Était-ce utile?

La solution

Impossible de faire fonctionner l'exemple en utilisant FileChannel.read(ByteBuffer) car ce n'est pas une lecture bloquante.J'ai cependant fait fonctionner le code ci-dessous :

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

Bien sûr, la même chose fonctionnerait comme une minuterie au lieu d'un thread, mais je laisse cela au programmeur.Je cherche toujours une meilleure façon, mais cela fonctionne pour moi pour le moment.

Oh, et je ferai une mise en garde avec :J'utilise 1.4.2.Oui, je sais, je suis encore à l'âge de pierre.

Autres conseils

Si vous souhaitez lire un fichier pendant son écriture et lire uniquement le nouveau contenu, ce qui suit vous aidera à obtenir la même chose.

Pour exécuter ce programme, vous le lancerez à partir de la fenêtre d'invite de commande/terminal et transmettrez le nom du fichier à lire.Il lira le fichier à moins que vous ne tuiez le programme.

java FileReader c:\monfichier.txt

Lorsque vous tapez une ligne de texte, enregistrez-la depuis le bloc-notes et vous verrez le texte imprimé dans la 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();
    }
}

Vous pouvez également consulter le canal Java pour verrouiller une partie d'un fichier.

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

Cette fonction du FileChannel c'est peut-être un début

lock(long position, long size, boolean shared) 

Une invocation de cette méthode bloquera jusqu'à ce que la région puisse être verrouillée

Je suis tout à fait d'accord avec La réponse de Josué, Tailleur est apte au travail dans cette situation.Voici un exemple :

Il écrit une ligne toutes les 150 ms dans un fichier, tout en lisant ce même fichier toutes les 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);
        }
    }
}

La réponse semble être non" ...et oui".Il ne semble pas y avoir de véritable moyen de savoir si un fichier est ouvert en écriture par une autre application.Ainsi, la lecture d'un tel fichier progressera jusqu'à ce que le contenu soit épuisé.J'ai suivi les conseils de Mike et écrit du code de test :

Writer.java écrit une chaîne dans un fichier, puis attend que l'utilisateur appuie sur Entrée avant d'écrire une autre ligne dans un fichier.L'idée étant qu'il pourrait être démarré, puis un lecteur peut être démarré pour voir comment il gère le fichier "partiel".Le lecteur que j'ai écrit est dans Reader.java.

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

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

Aucune garantie que ce code soit une bonne pratique.

Cela laisse l'option suggérée par Mike de vérifier périodiquement s'il y a de nouvelles données à lire dans le fichier.Cela nécessite ensuite l'intervention de l'utilisateur pour fermer le lecteur de fichiers lorsqu'il est déterminé que la lecture est terminée.Ou bien, le lecteur doit être informé du contenu du fichier et être capable de déterminer la condition de fin d'écriture.Si le contenu était XML, la fin du document pourrait être utilisée pour le signaler.

Pas Java en soi, mais vous pouvez rencontrer des problèmes lorsque vous avez écrit quelque chose dans un fichier, mais qu'il n'a pas encore été écrit - il se peut qu'il se trouve dans un cache quelque part, et la lecture à partir du même fichier peut ne pas vous donner les nouvelles informations.

Version courte - utilisez flush() ou quel que soit l'appel système pertinent pour vous assurer que vos données sont réellement écrites dans le fichier.

Remarque Je ne parle pas du cache disque au niveau du système d'exploitation - si vos données arrivent ici, elles devraient apparaître dans un read() après ce point.Il se peut que le langage lui-même mette en cache les écritures, en attendant qu'un tampon se remplisse ou que le fichier soit vidé/fermé.

Il existe une queue graphique Java Open Source qui fait cela.

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

Je ne l'ai jamais essayé, mais vous devriez écrire un scénario de test pour voir si la lecture d'un flux après avoir atteint la fin fonctionnera, qu'il y ait ou non plus de données écrites dans le fichier.

Y a-t-il une raison pour laquelle vous ne pouvez pas utiliser un flux d'entrée/sortie canalisé ?Les données sont-elles écrites et lues à partir de la même application (si oui, vous avez les données, pourquoi avez-vous besoin de lire à partir du fichier) ?

Sinon, lisez peut-être jusqu'à la fin du fichier, puis surveillez les modifications et cherchez là où vous vous êtes arrêté et continuez...mais faites attention aux conditions de course.

Vous ne pouvez pas lire un fichier ouvert à partir d'un autre processus à l'aide de FileInputStream, FileReader ou RandomAccessFile.

Mais utiliser directement FileChannel fonctionnera :

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;
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top