IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Entrée/Sortie Java Discussion :

Lecture et manipulation de "gros" fichiers.


Sujet :

Entrée/Sortie Java

  1. #1
    Nouveau membre du Club
    Homme Profil pro
    Inscrit en
    Mars 2006
    Messages
    87
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Mars 2006
    Messages : 87
    Points : 38
    Points
    38
    Par défaut Lecture et manipulation de "gros" fichiers.
    Bonjour à tous,

    Voici ma problématique:

    Je dois bosser sur des fichiers de type .csv avec une certaine norme.

    Pour m'aider dans ma tache, j'ai créer 2-3 classes java afin de faciliter les choses... Notamment une classe Ligne qui contient... une ligne du fichier d'origine, ainsi que diverses méthodes, et une classe Fichier, qui contient une LinkedList de Ligne et quelques autres trucs.

    Fichier me permet de manipuler mon fichier (changer une colonne, modifier telle colonne de telle ligne, enlever telle ligne, rajouter telle autre, etc...) et possède une méthode save(File sortie); qui permet de le sauvegarder là où on lui dit

    Le constucteur de Fichier ressemble à ça:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public Fichier(File file) throws IOException {
     
    		reader = new BufferedReader(new FileReader(file));
    		lignes = new LinkedList();
    		String buffer;
     
    		while ((buffer=reader.readLine())!=null) {
    			lignes.add(new Ligne(buffer));
    		}
     
    		reader.close();
    	}
    Donc, tout ceci, c'est bien sympa, ça me permet de manipuler le fichier comme je veux, mais j'ai a manipuler des fichiers de 200Mo des fois. Et là, j'ai une OutOfMemoryException heap size, comme vous devez vous en douter...


    La question est donc de savoir comment procéder pour que tout ceci fonctionne sans avoir a mettre un -Xmx512m par exemple...



    Merci beaucoup...

  2. #2
    Expert éminent sénior
    Avatar de adiGuba
    Homme Profil pro
    Développeur Java/Web
    Inscrit en
    Avril 2002
    Messages
    13 938
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Java/Web
    Secteur : Transports

    Informations forums :
    Inscription : Avril 2002
    Messages : 13 938
    Points : 23 190
    Points
    23 190
    Billets dans le blog
    1
    Par défaut
    Salut,


    Avant de commencer j'ai deux remarques sur ton code :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    public Fichier(File file) throws IOException {
     
    		lignes = new LinkedList();
     
    		BufferedReader reader = new BufferedReader(new FileReader(file));
    		try {
    			String buffer;
    			while ((buffer=reader.readLine())!=null) {
    				lignes.add(new Ligne(buffer));
    			}
    		} finally {
    			reader.close();
    		}
    	}

    Sinon par défaut les applications Java sont en -Xmx64m si je ne me trompe pas... donc à moins de trouver une solution miracle ce n'est pas possible de charger un fichier de 200Mo complètement en mémoire !


    Selon ce que tu fais de ces données il faudrait faire un traitement à la volée...


    a++

  3. #3
    Nouveau membre du Club
    Homme Profil pro
    Inscrit en
    Mars 2006
    Messages
    87
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Mars 2006
    Messages : 87
    Points : 38
    Points
    38
    Par défaut
    Merci pour le conseil du try/finally, j'aurais appris un truc aujourd'hui. (Du coup, je vais lire la FAQ sur les I/O, j'apprendrais plein de trucs)

    Sinon, concernant la solution miracle, ben c'est précisément ce que je cherche en fait

    Je tenterais bien de "bufferiser" tout ça, mais je vois pas trop comment en fait...

  4. #4
    Expert éminent sénior
    Avatar de adiGuba
    Homme Profil pro
    Développeur Java/Web
    Inscrit en
    Avril 2002
    Messages
    13 938
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Java/Web
    Secteur : Transports

    Informations forums :
    Inscription : Avril 2002
    Messages : 13 938
    Points : 23 190
    Points
    23 190
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par ::Fistons Voir le message
    Je tenterais bien de "bufferiser" tout ça, mais je vois pas trop comment en fait...
    Ben tu as deux solutions :
    • Soit tu continues comme maintenant en chargeant tout en mémoire, et dans ce cas tu es obligé d'augmenter la mémoire.
    • Soit tu traites les données au moment où tu les lis sans les conserver (si c'est possible bien sûr), par exemple :
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      4
      5
      6
      7
      8
      9
      		BufferedReader reader = new BufferedReader(new FileReader(file));
      		try {
      			String buffer;
      			while ((buffer=reader.readLine())!=null) {
      				// Traitement de buffer
      			}
      		} finally {
      			reader.close();
      		}


    a++

  5. #5
    Nouveau membre du Club
    Homme Profil pro
    Inscrit en
    Mars 2006
    Messages
    87
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Mars 2006
    Messages : 87
    Points : 38
    Points
    38
    Par défaut
    La deuxième solution n'est malheureusement pas possible...

    Par contre, je viens de tomber sur la classe Scanner de l'API standard, je vais peut être pouvoir en faire quelque chose... Je jette un coup d'œil et je vous dit


    Edit: Bon, il semblerait que ça suffise pas en fait... Me faudrait un mixte entre RandomAccessFile et BufferedReader...

  6. #6
    Expert éminent sénior
    Avatar de adiGuba
    Homme Profil pro
    Développeur Java/Web
    Inscrit en
    Avril 2002
    Messages
    13 938
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Java/Web
    Secteur : Transports

    Informations forums :
    Inscription : Avril 2002
    Messages : 13 938
    Points : 23 190
    Points
    23 190
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par ::Fistons Voir le message
    Par contre, je viens de tomber sur la classe Scanner de l'API standard, je vais peut être pouvoir en faire quelque chose... Je jette un coup d'œil et je vous dit
    Ben le problème sera le même : si tu veux charger 200Mo en mémoire, il ta faut au moins 200Mo de mémoire...


    a++

  7. #7
    Nouveau membre du Club
    Homme Profil pro
    Inscrit en
    Mars 2006
    Messages
    87
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Mars 2006
    Messages : 87
    Points : 38
    Points
    38
    Par défaut
    Oui, je me doute, mais je pensais à ma solution de buffering...

    Sinon, je pense à un truc surement débile, mais avec les JDO, y'a peut être moyen de faire ce que je veux? Non?

  8. #8
    Expert éminent sénior
    Avatar de adiGuba
    Homme Profil pro
    Développeur Java/Web
    Inscrit en
    Avril 2002
    Messages
    13 938
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Java/Web
    Secteur : Transports

    Informations forums :
    Inscription : Avril 2002
    Messages : 13 938
    Points : 23 190
    Points
    23 190
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par ::Fistons Voir le message
    Oui, je me doute, mais je pensais à ma solution de buffering...
    Tu veux dire quoi par "buffering" précisément ?

    Citation Envoyé par ::Fistons Voir le message
    Sinon, je pense à un truc surement débile, mais avec les JDO, y'a peut être moyen de faire ce que je veux? Non?
    Et au final tu veux faire quoi précisément ?

    a++

  9. #9
    Nouveau membre du Club
    Homme Profil pro
    Inscrit en
    Mars 2006
    Messages
    87
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Mars 2006
    Messages : 87
    Points : 38
    Points
    38
    Par défaut
    Par buffering, j'entends, par exemple, ne charger que 200 lignes en mémoires.

    Cependant, si je cherche à accéder à une ligne qui n'est pas en mémoire... Je vais m'amuser un peu :\


    Avec les JDOs, je sais pas trop encore quoi faire avec mais le nom "Java Data Objects" m'inspirait pas mal... (J'avais une idée de mapping fichier <-> object qui m'aurait bien fait plaisir...)


    Edit: Ok, les JDO ne vont pas me servir à grand chose. Conclusion: je suis dans la merde.

  10. #10
    Modérateur
    Avatar de dinobogan
    Homme Profil pro
    ingénieur
    Inscrit en
    Juin 2007
    Messages
    4 073
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France

    Informations professionnelles :
    Activité : ingénieur
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 4 073
    Points : 7 163
    Points
    7 163
    Par défaut
    Quelle est la manipulation qui t'empêche de traiter le fichier ligne par ligne ?
    Car dans ton premier post, rien n'oblige à tout monter en mémoire :
    (changer une colonne, modifier telle colonne de telle ligne, enlever telle ligne, rajouter telle autre, etc...)
    C'est le "etc" que j'aimerai que tu détailles

  11. #11
    Nouveau membre du Club
    Homme Profil pro
    Inscrit en
    Mars 2006
    Messages
    87
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Mars 2006
    Messages : 87
    Points : 38
    Points
    38
    Par défaut
    Typiquement, j'aimerais qu'on puisse le voir dans un JTable et le manipuler par ce biais

  12. #12
    Membre chevronné

    Homme Profil pro
    Architecte logiciel
    Inscrit en
    Novembre 2006
    Messages
    1 252
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Architecte logiciel
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Novembre 2006
    Messages : 1 252
    Points : 1 954
    Points
    1 954
    Par défaut
    Ce que tu as besoin d'après ce que j'ai lu c'est d'accéder par tronçons à un fichier textuel. Ainsi, il te faut l'implémentation de cette interface:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    interface MyFileDao {
       ArrayList<String> getHead( int count );
       ArrayList<String> getChunk( int start, int end );
       ArrayList<String> getTail( int count );
    }
    Ben, techniquement, aucune chance d'y parvenir sans parcourir tout le fichier, sauf si les lignes ont une longueur fixe. Même un l'utilisation d'un cache n'est pas gagné, en tout cas faut la jouer fine. Stocker par contiguité de bloc, vérifier l'existance par intersection de façon a ne faire que la lecture minimale et réalimenter le cache en créant les blocs les plus gros... enfin, pas gagné.

  13. #13
    Nouveau membre du Club
    Homme Profil pro
    Inscrit en
    Mars 2006
    Messages
    87
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Mars 2006
    Messages : 87
    Points : 38
    Points
    38
    Par défaut
    C'est déjà un bonne piste.

    D'ou vient cette interface? Et avec quoi peux t'on l'utiliser?

    Merci pour ton aide?

  14. #14
    Expert éminent sénior
    Avatar de adiGuba
    Homme Profil pro
    Développeur Java/Web
    Inscrit en
    Avril 2002
    Messages
    13 938
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Java/Web
    Secteur : Transports

    Informations forums :
    Inscription : Avril 2002
    Messages : 13 938
    Points : 23 190
    Points
    23 190
    Billets dans le blog
    1
    Par défaut
    Je pense qu'il veut dire que tu ne conserve en mémoire que les "adresses" des données dans le fichier, et que tu accèdes au fichiers à chaque fois que tu en as besoin.

    Ce doit être faisable en utilisant un RandomAccessFile ou un MappedByteBuffer...


    a++

  15. #15
    Membre chevronné

    Homme Profil pro
    Architecte logiciel
    Inscrit en
    Novembre 2006
    Messages
    1 252
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Architecte logiciel
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Novembre 2006
    Messages : 1 252
    Points : 1 954
    Points
    1 954
    Par défaut
    Pas testé, doit être légèrement buggé, mais l'esprit est là:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    import java.io.IOException;
    import java.util.List;
     
    public interface MyFileDAO {
        List<String> getHead(Integer count) throws IOException;
     
        List<String> getChunk(Integer start, Integer end) throws IOException;
     
        List<String> getTail(Integer count) throws IOException;
     
        Integer getNbLines();
    }
     
     
     
    import java.io.File;
    import java.io.IOException;
    import java.io.RandomAccessFile;
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
     
    public class MyFileDAOImpl implements MyFileDAO {
     
        private final Map<Integer, Long> indexes;
        private final RandomAccessFile raf;
        private final File file;
     
        private Integer nbLines;
     
        public MyFileDAOImpl(File f) throws IOException {
            indexes = new HashMap<Integer, Long>(500);
            file = f;
            raf = new RandomAccessFile(f, "r");
     
            computeIndexes();
        }
     
        private void computeIndexes() throws IOException {
            nbLines = 0;
            while (raf.getFilePointer() != raf.length()) {
                indexes.put(nbLines, raf.getFilePointer());
                raf.readLine();
                nbLines++;
            }
        }
     
        public List<String> getChunk(Integer start, Integer end) throws IOException {
            final List<String> lines = new ArrayList<String>(start - end + 1);
     
            final Long pos = indexes.get(start);
     
            raf.seek(pos);
     
            for (int i = start; i <= end; ++i) {
                lines.add(raf.readLine());
            }
     
            return lines;
        }
     
        public List<String> getHead(Integer count) throws IOException {
            return getChunk(0, count);
        }
     
        public List<String> getTail(Integer count) throws IOException {
            return getChunk(getNbLines() - count, getNbLines() - 1);
        }
     
        public Integer getNbLines() {
            return nbLines;
        }
     
    }

  16. #16
    Nouveau membre du Club
    Homme Profil pro
    Inscrit en
    Mars 2006
    Messages
    87
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Mars 2006
    Messages : 87
    Points : 38
    Points
    38
    Par défaut
    Ca me parait etre un très bonne piste tout cela, je vais voir tout ça, je vous tiens au courant

  17. #17
    Nouveau membre du Club
    Homme Profil pro
    Inscrit en
    Mars 2006
    Messages
    87
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Mars 2006
    Messages : 87
    Points : 38
    Points
    38
    Par défaut
    Alors, c'est assez génial, cette méthode me permet de faire tout ce que je veux... en lecture.

    En écriture, c'est déjà un peu plus chaud... Disons que le PC mouline a fond avant de me faire un OutOfMemoryException heap size

    Mon problème est le suivant...
    J'aimerais pouvoir:
    - Ajouter une ligne à la fin du fichier
    - Enlever un ligne, n'importe où dans le fichier
    - Changer une colonne, d'une ou plusieurs ligne (j'ai tenté d'opérer a un "remplacement" de ligne, ce qui m'a donné le heap size du dessus

    Merci pour votre aide en tout cas

  18. #18
    Expert éminent sénior
    Avatar de adiGuba
    Homme Profil pro
    Développeur Java/Web
    Inscrit en
    Avril 2002
    Messages
    13 938
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Java/Web
    Secteur : Transports

    Informations forums :
    Inscription : Avril 2002
    Messages : 13 938
    Points : 23 190
    Points
    23 190
    Billets dans le blog
    1
    Par défaut
    Salut,


    Tommy31 je n'ai pas testé ton code mais il y a quelques petits "problèmes" potentiels quand à la gestion de la fermeture des flux.


    • Le premier point consiste simplement à proposer une méthode close() qui permette à l'utilisateur de la classe de fermer le flux :
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      public void close() throws IOException {
      	this.raf.close();
      }
      Note que si tu es sous Java 5.0 ou plus tu peux implémenter l'interface Closeable :
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      public class MyFileDAOImpl implements MyFileDAO, Closeable {
    • Tu pourrais également gérer la finalisation de la classe comme garde fou (le fichier sera fermé par le GC même s'il n'est pas proprement fermé) :
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      public void finalize() throws Exception {
      	this.close();
      }

    • Enfin, dernier point : si une exception est remontée pendant l'appel de la méthode computeIndexes() de ton constructeur, tu n'auras pas d'objet FileDAOImpl en mémoire mais ton fichier sera bel et bien ouvert ! Si tu utilises un finalize() cela ne sera fait qu'à la libération mais comme tu ne contrôle pas ce moment cela peut être problématique (sur certains système le simple fait de conserver le fichier ouvert en lecture peut amener des restrictions en écriture : impossible de le modifier ou de le supprimer).

      Un petit try/finally suffit à régler ce problème :
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
          public MyFileDAOImpl(File f) throws IOException {
              indexes = new HashMap<Integer, Long>(500);
              file = f;
      	boolean success = false;
              raf = new RandomAccessFile(f, "r");
       	try {
      	        computeIndexes();
      		success = true;
      	} finally {
      		if (!success) {
      			raf.close();
      		}
      	}
          }
      Si success vaut false cela signifie que le bloc try ne s'est pas exécuté correctement et qu'une exception est remontée. On la laisse remonter mais avant on ferme proprement le fichier via le bloc finally



    ::Fistons Pour toutes opérations de modification d'un fichier il faut en créer un nouveau, à moins que tu ne fasses une modifications d'un octet par un autre (ie: remplacement et non pas insertion).

    a++

  19. #19
    Membre chevronné

    Homme Profil pro
    Architecte logiciel
    Inscrit en
    Novembre 2006
    Messages
    1 252
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Architecte logiciel
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Novembre 2006
    Messages : 1 252
    Points : 1 954
    Points
    1 954
    Par défaut
    Oui, j'ai négligé cet aspect !
    Ce serait bien de poster le code dans http://www.developpez.net/forums/showthread.php?t=13730

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo