Partage
  • Partager sur Facebook
  • Partager sur Twitter

[TUTO] Les flux/fichiers : la lecture

Comment lire proprement, simplement et efficacement les flux/fichiers

    12 avril 2016 à 23:10:08

    Le problème revient très fréquemment sur le forum. Ne pouvant modifier le cours, et pour éviter de répéter tout le temps les mêmes choses, je me permets de créer cette discussion autour du sujet.
    Ce message sera amené à évoluer au cours du temps.
    N'hésitez pas à remonter tout point à corriger/améliorer/ajouter !

    Méthode ancestrale (Java <= 6)

    Il est possible d'obtenir le résultat sous forme de byte[] ou String, ici nous le lierons ligne par ligne afin de montrer toutes les étapes clés du traitement :

    InputStream inputStream = null;
    InputStreamReader inputStreamReader = null;
    BufferedReader bufferedReader = null;
    try {
        inputStream = new FileInputStream("/path/to/file.ext");
        inputStreamReader = new InputStreamReader(inputStream);
        bufferedReader = new BufferedReader(inputStreamReader);
        
        String line;
        while ((line = bufferedReader.readLine()) != null)
            doSomething(line);
    } catch (IOException e) {
    	gestion(e);
    } finally {
        if (bufferedReader != null)
            try {
                bufferedReader.close();
            } catch (IOException e) {
                gestion(e);
            }
        if (inputStreamReader != null)
            try {
                inputStreamReader.close();
            } catch (IOException e) {
                gestion(e);
            }
        if (inputStream != null)
            try {
                inputStream.close();
            } catch (IOException e) {
                gestion(e);
            }
    }

    Remarques :

    • Fermer les flux dans l'ordre inverse d'ouverture
    • Tester la nullité des flux avant fermeture : si une erreur se produit lors de l'instanciation du 2nd flux, le 3ème sera null

    Erreurs courantes :

    • new BufferedInputStream(new FileInputStream(...)) : si vous ne savez pas à quoi sert BufferedInputStream et que vous n'en avez pas l'utilité, alors ne l'utilisez pas !
    • new FileInputStream(new File("/path/to/file.ext")) : vous pouvez directement instancier le FileInputStream qui possède un constructeur prenant le chemin du fichier sous forme de Stringnew FileInputStream("/path/to/file.ext")

    Critiques :

    • - Extrêmement verbeux : 32 lignes minimum
    • - Source d'erreur : 99% des développeurs ne savent pas gérer les close() proprement
    • - Multiple gestion des erreurs (duplication de code)

    Try-with-resources (Java 7)

    Documentation Oracle

    Java 7 introduit le try-with-resources permettant la gestion simple et sécurisée des flux.
    L'objectif est de déléguer aux compilateur tous les close() contenus dans le finally.
    Cela est similaire au with de Python ou using de .Net.

    try (InputStream inputStream = new FileInputStream("/path/to/file.ext");
         InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
         BufferedReader bufferedReader = new BufferedReader(inputStreamReader)) {
        String line;
        while ((line = bufferedReader.readLine()) != null)
            doSomething(line);
    } catch (IOException e) {
        gestion(e);
    }

    Remarques :

    • Déclarer les flux séparément dans le try : les flux doivent être fermés 1 par 1.
      Si vous faites BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream("/path/to/file.ext"))) alors uniquement le BufferedReader sera fermé.
      Généralement les flux sont proprement implémentés : fermer l'englobeur fera appel à la fermeture de l'englobé. Mais cela n'est pas obligatoire, et donc on pourrait rencontrer une implémentation pouvant poser problème.

    Méthodes utilitaires

    Ces techniques permettent de lire des flux en 1 instruction, sans avoir à ré-implémenter le parcours du flux.

    Common IO (Apache Commons)

    Site web

    Il s'agit probablement de la bibliothèque la plus connue et la plus utilisée pour lire les flux.
    Elle propose, depuis la version 1.1 sortie en 2005 (il y a plus de 10 ans), les principales méthodes de lecture. 

    byte[] bytes = IOUtils.toByteArray(new FileInputStream("/path/to/file.ext"));
    
    String content = IOUtils.toString(new FileInputStream("/path/to/file.ext"));
    
    List<String> lines = IOUtils.readLines(new FileInputStream("/path/to/file.ext"));
    
    for (Iterator it = IOUtils.lineIterator(new FileInputStream("/path/to/file.ext"), Charset.defaultCharset()); it.hasNext();) {
        String line = it.next();
        doSomething(line);
    }

    Remarques :

    • Ces méthodes lèvent une IOException en cas d'erreur, à gérer dans un catch.
    • Les flux ne sont pas fermés : il suffit d'appliquer un simple try-with-resources sur la méthode englobante.
      "Wherever possible, the methods in this class do not flush or close the stream. This is to avoid making non-portable assumptions about the streams' origin and further use. Thus the caller is still responsible for closing streams after use."

    Voici donc le code minimal d'utilisation :

    try (InputStream inputStream = new FileInputStream("/path/to/file.ext")) {
        YYY res = IOUtils.XXX(inputStream);
    } catch (IOException e) {
        gestion(e);
    }

    Non-blocking I/O (Java 7)

    Le nouveau package java.nio a été introduit afin de remplacer l'ancienne API java.io, proposant de nouvelles fonctionnalités de lecture de fichiers.
    Elle introduit notamment les classes utilitaires Files et Paths.

    byte[] bytes = Files.readAllBytes(Paths.get("/path/to/file.ext"));
    
    List<String> lines = Files.readAllLines(Paths.get("/path/to/file.ext"));
    
    Stream<String> stream = Files.lines(Paths.get("/path/to/file.ext"));

    Remarques :

    • Ces méthodes lèvent une IOException en cas d'erreur
    • Il n'y a pas de gestion de (File)InputStream à effectuer
    • Ces méthodes ne permettent que de manipuler les fichier.
      Pour manipuler des InputStream, voir la bibliothèque Apache.
    • Cela ne permet que de lire des fichiers. Il existe surement des méthodes pour lire flux en général, mais je n'ai pas encore assez étudié la question.

    Guava

    Site web

    Cette bibliothèque de Google propose également de très nombreuses fonctionnalités, dont la gestion des flux.

    Les fonctionnalités sont proches de celle d'Apache.

    Autres fonctionnalités

    Les flux de sortie (OutputStream), ainsi que la copie de flux (InputStream > OutputStream) sont aussi des opérations fastidieuses, qui pourront être effectuées aussi simplement avec les mêmes bibliothèques citées précédemment.
    Je vous laisse parcourir les différentes méthodes de votre côté. 

    -
    Edité par Pinguet62 24 avril 2016 à 16:23:09

    • Partager sur Facebook
    • Partager sur Twitter
    Angular 2 est l'avenir, jQuery c'est de la merde !!! - Java 8 c'est l'an 2016+ (programmez en 1 ligne)
      13 avril 2016 à 8:52:26

      Sympa ce mini-tuto. J'y ai appris quelques trucs intéressants. :)
      • Partager sur Facebook
      • Partager sur Twitter
        13 avril 2016 à 10:25:17

        Aaaaaaaamen ^^

        Merci pour ce tuto !

        • Partager sur Facebook
        • Partager sur Twitter

        [TUTO] Les flux/fichiers : la lecture

        × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
        × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
        • Editeur
        • Markdown