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 / Ecriture Flux (Fichier, socket, etc.)


Sujet :

Entrée/Sortie Java

  1. #1
    Futur Membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Mars 2012
    Messages
    6
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mars 2012
    Messages : 6
    Points : 6
    Points
    6
    Par défaut Lecture / Ecriture Flux (Fichier, socket, etc.)
    Bonjour,

    Je cherche à réaliser une classe permettant de lire simplement un flux provenant d'un fichier ou d'une socket.

    Pour cela, j'ai commencé à créer la fonction readAll(BufferedReader), qui permet de lire entièrement le contenu du flux.

    Remarque : Je ne peux utiliser la fonction BufferedReader.readLine() car il m'est impossible de connaitre le caractère qui a provoqué la sortie de la fonction ('\r', '\n' ou '\r' suivi de '\n'), a moins de lire 2 fois le flux et de stocker les positions des fins de ligne (ce qui me semble impossible pour les sockets).
    Je n'ai pas non plus opté pour une lecture caractère par caractère, étant donné la lenteur du traitement. J'ai donc choisi d'utiliser la fonction BufferedReader.read(char[]) avec un buffer assez grand pour ameliorer la vitesse (même si ça ne me satisfais pas vraiment, en comparaison avec le readLine(), qui reste plus rapide).

    Voici donc le code actuel de la fonction readAll(BufferedReader) (je l'ai mise en static pour les tests):

    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
    public static String readAll(BufferedReader reader) throws IOException, NullPointerException
    {
    	char[] buffer = new char[1024 * 8];
    	StringBuilder builder = new StringBuilder();
    	int len = 0;
     
    	if(reader == null)
    		throw new NullPointerException("Erreur fonction readAll(BufferedReader) : le parametre vaut null.");
     
    	try
    	{
    		while((len = reader.read(buffer)) > 0)
    			builder.append(String.valueOf(buffer), 0, len);
    	}
    	catch(IOException eRead)
    	{
    		throw new IOException("Erreur fonction readAll(BufferedReader) : Une erreur est survenue lors de la lecture du reader.", eRead.getCause());
    	}
    	finally
    	{
    		try
    		{
    			reader.close();
    		}
    		catch(IOException eClose)
    		{
    			throw new IOException("Erreur fonction readAll(BufferedReader) : Une erreur est survenue lors de la fermeture du reader.", eClose.getCause());
    		}
    	}
     
    	return builder.toString();
    }
    Même si je n'ai pas une grande expérience du Java, je ne pense pas m'être trompé ici.

    Pour vérifier que cette fonction s'exécute correctement, j'ai donc créé une fonction write(BufferedWriter, String), toujours en static, dont voici le 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
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    public static void write(BufferedWriter writer, String donnees) throws IOException, NullPointerException
    {
    	if(donnees == null)
    		throw new NullPointerException("Erreur fonction write(BufferedWriter, String) : la chaine en parametre vaut null.");
    	if(writer == null)
    		throw new NullPointerException("Erreur fonction write(BufferedWriter, String) : le writer en parametre vaut null.");
     
    	try
    	{
    		writer.write(donnees);
    		try
    		{
    			writer.flush();
    		}
    		catch(IOException eFlush)
    		{
    			throw new IOException("Erreur fonction write(BufferedWriter, String) : impossible de flush().", eFlush.getCause());
    		}
    	}
    	catch(IOException eWrite)
    	{
    		throw new IOException("Erreur fonction write(BufferedWriter, String) : impossible d'ecrire dans le writer.", eWrite.getCause());
    	}
    	finally
    	{
    		try
    		{
    			writer.close();
    		}
    		catch(IOException eClose)
    		{
    			throw new IOException("Erreur fonction write(BufferedWriter, String) : Une erreur est survenue lors de la fermeture du writer.", eClose.getCause());
    		}
    	}
    }
    Rien (je pense) de compliqué ici non plus.
    Je teste donc ces deux fonction grâce à ce main :
    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
    public static void main(String[] args) 
    {
    	String s = null, entree = "C:\\UN\\FICHIER\\EXECUTABLE\\A\\COPIER.exe", sortie = "C:\\UN\\FICHIER\\EXECUTABLE\\COPIE.exe";
    	System.out.println("Debut Programme");
    	try
    	{
    		BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(new File(entree))));
    		BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new File(sortie))));
    		s = readAll(br);
    		write(bw, s);
    		System.out.println("Ecriture Ok");
    	}
    	catch(Exception e)
    	{
    		e.printStackTrace();
    	}
    	System.out.println("Fin Programme");
    }
    Je ne teste pas l'existence des fichiers, ni s'il s'agit de répertoires, je sais que je ne me suis pas trompé ici. Je n'ai pas mis de commentaires non plus mais j'ai essayé de rendre parlantes les Exceptions.
    Je tiens aussi à préciser que je n'ai réalisé aucun test sur des sockets, donc il est possible que cela ne fonctionne pas avec celles ci.

    Lors de l'exécution du code, aucune exception n'est levée, mais j'obtiens un problème : certain caractères "apparaissent" dans le fichier de sortie, lorsque je compare les fichiers avec un éditeur de texte (Notepad++). Il s'agit en fait de points d'interrogation qui apparaissent indépendamment de la taille du buffer et des caractères qui précèdent et qui suivent, mais qui se retrouvent toujours aux mêmes endroits.


    Après beaucoup de tests, il semble que cela vienne du reader.read(buffer), et non pas du passage du char[] au StringBuilder comme je le pensais au départ.
    J'ai pensé aussi qu'il pouvait s'agir d'un problème d'encodage mais les points d'interrogation "apparaissent" bien : je veux dire par là que les deux caractères qui les entourent dans le fichier de sortie sont bien présents dans le fichier d'entrée, mais qu'il sont collés dans le fichier d'entrée.

    Je commence sérieusement à désespérer après plusieurs heures de recherches sur des solutions ou des manières alternatives, je pensais être moins embêté pour simplement copier des fichiers.

    Cordialement.

  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
    Citation Envoyé par LittleT Voir le message
    J'ai donc choisi d'utiliser la fonction BufferedReader.read(char[]) avec un buffer assez grand pour ameliorer la vitesse (même si ça ne me satisfais pas vraiment, en comparaison avec le readLine(), qui reste plus rapide).
    Cette solution me semble tout à fait adapté !
    Je ne pense pas que le BufferedReader readLine() soit plus performant. Surtout qu'il utilise lui aussi un buffer de 8192...


    Sinon quelques remarques général sur ton code :
    • Pourquoi utiliser un BufferedReader si tu n'en a pas l'utilité ?
      Autant utiliser directement un Reader. Cela t'évite un double-buffer inutile (le tiens + celui du BufferedReader).

    • C'est bien de vérifier la nullité des objets, mais tant qu'à fait autant le faire au tout début de la méthode, avant de créer tes buffers...

    • Je n'aime pas vraiment ta gestion des exceptions.
      Pourquoi remonter tes propres exceptions avec des messages pas très clair ?
      Surtout qu'en plus tu l'associes pas avec l'exception d'origine mais mais la "cause" qui n'existe probablement pas !

      Du coup tu perds toute l'information sur l'origine exact du problème.
      Par exemple dans le cas d'une déconnexion réseau, au lieu d'avoir :
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      java.io.IOException: Connection reset by peer
      tu aura seulement :
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      Erreur fonction readAll(BufferedReader) : Une erreur est survenue lors de la lecture du reader.
      Ok une erreur est survenue ! Mais tu ne sais pas laquelle !!!
      Il faut modifier ton code source pour avoir l'erreur original !
      Ok tu mets le nom de la méthode dans le message, mais c'est inutile car on a cela via le stacktrace...

      Perso je ne comprend pas l'utilité de tes exceptions, autant laisser remonter les exceptions originales. Ou vraiment si tu veux les personnaliser garde la référence de l'exception originale (et non pas de sa cause qui peut être null) :
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      4
      catch (IOException e) {
          // throw new IOException("message", e.getCause());
             throw new IOException("message");
      }
      Mais bon perso je trouve cela inutile...


    • Tu crées inutilement un objet String dans ta boucle. Tu parlais de différence de perfs avec le BufferedReader, cela ne m'étonnerais pas que cela viennent de là :
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      // builder.append(String.valueOf(buffer), 0, len);
         builder.append(buffer, 0, len);

    • Enfin perso je ne fermerais pas le Reader dans la méthode.
      J'essaye toujours de fermer les ressources là où je les ai ouverte, via un try/finally pour être sûr de bien la fermer dans tous les cas.




    Bref perso je ferais plutôt quelque chose comme cela :
    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
    	public static String readAll(File file)
    			throws IOException, NullPointerException {
    		final Reader reader = new FileReader(file);
    		try {
    			return readAll(reader);
    		} finally {
    			reader.close();
    		}
    	}
     
     
    	public static String readAll(Reader reader)
    		throws IOException, NullPointerException {
    		if (reader == null)
    			throw new NullPointerException("reader is null");
     
    		char[] buffer = new char[1024 * 8];
    		StringBuilder builder = new StringBuilder();
    		int len = 0;
     
    		while ((len = reader.read(buffer)) > 0) {
    			builder.append(buffer, 0, len);
    		}
    		return buffer.toString();
    	}
    A noter qu'avec Java 7 tu peux encore simplifier tout cela avec le try-with-ressources ou la méthode Objects.requireNonNull()...





    Maintenant pour en revenir sur ton problème : il y a de fortes chances que cela viennent d'un problème d'encodage.
    En effet tu ne spécifies pas l'encodage du fichier, et c'est donc l'encodage par défaut du système qui est utilisé. Si celui-ci est différent que celui du fichier, alors certains caractères peuvent être mal décodé, et donc mal réencodé quand tu écris le fichier...

    Bref : tu dois spécifier l'encodage de tes fichiers textes, par exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    	Reader reader = new InputStreamReader(
    			new FileInputStream(file), "utf-8");
    Par contre il n'y a aucun moyen simple de détecter l'encodage d'un fichier !



    Citation Envoyé par LittleT Voir le message
    Je commence sérieusement à désespérer après plusieurs heures de recherches sur des solutions ou des manières alternatives, je pensais être moins embêté pour simplement copier des fichiers.
    Tu ne fait pas que copier des fichiers !
    Tu les décodes via un charset lors de la lecture, et tu les réencodes à l'écriture.

    Si tu veux seulement copier des fichiers il est plus prudent de passer par des bytes qui t'évites ces encodages/décodages...


    a++

  3. #3
    Modérateur

    Profil pro
    Inscrit en
    Septembre 2004
    Messages
    12 547
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2004
    Messages : 12 547
    Points : 21 602
    Points
    21 602
    Par défaut
    J'ajoute que flush() est généralement inutile.
    Et qu'il l'est toujours avant un close(), puisque l'une des responsabilités de close() est de traiter tout ce qui restait en suspend pour s'en débarrasser définitivement.
    N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

  4. #4
    Futur Membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Mars 2012
    Messages
    6
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mars 2012
    Messages : 6
    Points : 6
    Points
    6
    Par défaut
    Merci à vous deux pour ces réponses qui m'ont bien aidé

    A propos de vos remarques :
    • Concernant les performances du readLine(), j'ai consulté ce site car honnêtement je n'avais aucune idée du mécanisme à l'intérieur de chacune des fonctions.
    • A vrai dire pour l'utilisation du BufferedReader, c'est seulement car je suis un peu perdu, car j'ai l'impression qu'il y a d'innombrables moyens de lire un stream. J'ai trouvé au départ la fonction readLine() très pratique et depuis j'utilisais donc tout le temps le BufferedReader.
    • Concernant la vérification de la nullité des objets, j'ai préféré faire tout de même les déclarations avant, une vieille habitude du C où je crois qu'il est déconseillé de faire des déclarations au "milieu" du code
    • J'ai en effet mal géré les exceptions, en pensant les rendre plus claires j'ai masqué certaines infos. Au début c'était surtout pour savoir quelle fonction pouvait lever tel ou tel type d'exception, plutôt que de mettre throws IOException etc. que je trouve assez obscur.
    • Concernant la création de la String dans la boucle, c'est simplement que je n'ai pas fait attention qu'il y avait une surcharge du append prenant en paramètre un char[]
    • Je me suis aussi posé la question de la fermeture du reader dans la fonction elle même, toutefois étant donné qu'on le lit jusqu'à la fin, il aurait fallu qu'il ait été marqué auparavant avec une limite aléatoire (ce que j'ai jugé peu probable).
    • Enfin pour le flush(), c'est quelque chose que j'ignorais totalement donc je l'ai mis par acquis de conscience


    Voici donc mon nouveau code (fonctionnel) mais certains points me gênent toujours :
    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
    public static byte[] readAll(InputStream reader) throws IOException, NullPointerException, NegativeArraySizeException 
    	{
    		if(reader == null)
    			throw new NullPointerException("'InputStream reader' is null.");
     
    		int len = 0, tailleLue = 0, pas = 1024 * 1024 * 4;
    		byte[] buffer = new byte[pas];
    		long debut = System.currentTimeMillis(), fin;
     
    		while((len = reader.read(buffer, tailleLue, buffer.length - tailleLue)) > 0)//Throws IOException
    		{
    			tailleLue += len;
    			buffer = Arrays.copyOf(buffer, tailleLue + pas); //Throws NegativeArraySizeException , NullPointerException
    		}
     
    		fin = System.currentTimeMillis();
    		System.out.println(fin - debut);
     
    		return Arrays.copyOf(buffer, tailleLue); //Throws NegativeArraySizeException , NullPointerException
    	}
     
     
    	public static void write(OutputStream writer, byte[] data) throws IOException, NullPointerException
    	{
    		if(writer == null)
    			throw new NullPointerException("'OutputStream writer' is null.");
    		if(data == null)
    			throw new NullPointerException("'byte[] data' is null.");
     
    		writer.write(data); //Throws IOException
    	}
     
    	public static void main(String[] args) {
    		byte[] donnees = null;
    		String entree = "C:\\FICHIER\\ENTREE.exe", sortie = "C:\\FICHIER\\SORTIE.exe";
    		System.out.println("Debut Programme");
     
    		InputStream is = null;
    		OutputStream os = null;
    		try
    		{
     
    			is = new FileInputStream(new File(entree));
    			os = new FileOutputStream(new File(sortie));
    			donnees = readAll(is);
    			System.out.println("Lecture OK");
    			write(os, donnees);
    			System.out.println("Ecriture OK");
    		}
    		catch(Exception e)
    		{
    			e.printStackTrace();
    		}
    		finally
    		{
    			try
    			{
    				is.close();
    				os.close();
    				System.out.println("Fermeture OK");
    			}
    			catch(Exception e)
    			{
    				e.printStackTrace();
    			}
    		}
    		System.out.println("Fin Programme");
    	}
    Comme on peut le voir, j'ai effectué des tests et lorsque j'ai un pas trop petit (fonction readAll()), la copie dure trop longtemps.
    Exemple : pour copier un fichier d'environ 40 Méga Octets avec un pas de 8 Kilo Octets (8 * 1024 * taille d'un byte, qui est comme tu l'as dis adiGuba la taille standart pour un buffer de BufferedReader), le copie était tellement longue que j'ai du arrêter avant la fin ( + d'une minute) et en plus la place occupée par le programme en RAM augmentait (dans les 400Mo vers la fin je crois). Toutefois, ça me gène d'allouer directement un buffer de 4Mo (1024 * 1024 * 4 * taille d'un byte) comme dans le code ici, je trouve ça assez brutal comme méthode. Toutefois, avec un tel buffer, je met environ entre 500ms et 1s pour copier les 40 Méga Octets et moins de 500ms lorsque je prend un buffer de taille supérieure à la taille du fichier. Je trouve aussi que c'est assez moche de faire de multiples appels à Arrays.copyOf(), mais ça à l'air de fonctionner parfaitement.
    Si vous avez d'autres méthodes plus propres mais tout autant fonctionnelles, je prend.
    N'hésitez pas non plus à critiquer tout le code comme vous l'avez fait si quelque chose vous gêne encore, je suis là aussi preneur

    EDIT : En plus ce que je n'aime pas dans ma méthode, c'est que la totalité de ce qui est à copier se trouve à un moment dans la RAM. Pour des fichiers de plus d'1 GO ça commence à poser problème..

    Cordialement

  5. #5
    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 LittleT Voir le message
    Concernant les performances du readLine(), j'ai consulté ce site car honnêtement je n'avais aucune idée du mécanisme à l'intérieur de chacune des fonctions.
    Sauf qu'il utilise au maximum un buffer de 4086... alors que BufferedReader utilise par défaut un buffer de 8192. Soit le double !

    Plus généralement plutôt que de chercher à tout prix ce qui est le plus performant, il faut plutôt chercher à utiliser ce qui est utile

    Citation Envoyé par LittleT Voir le message
    A vrai dire pour l'utilisation du BufferedReader, c'est seulement car je suis un peu perdu, car j'ai l'impression qu'il y a d'innombrables moyens de lire un stream. J'ai trouvé au départ la fonction readLine() très pratique et depuis j'utilisais donc tout le temps le BufferedReader.
    Y'a pas de soucis ! Je sais bien que l'API peut sembler énorme et complexe
    C'est juste que si le BufferedReader n'est pas obligatoire, c'est bête de l'imposer...

    Imagine que demain tu vas utiliser un Reader qui lit des données depuis une base de donnée par exemple. Tout est déjà bufférisé mais tu ne pourras pas utiliser directement ce Reader avec ta méthode car elle impose inutilement un BufferedReader...

    Dans une méthode c'est toujours bien de conserver des types très abstrait, cela permet plus de liberté d'utilisation...

    Citation Envoyé par LittleT Voir le message
    Concernant la vérification de la nullité des objets, j'ai préféré faire tout de même les déclarations avant, une vieille habitude du C où je crois qu'il est déconseillé de faire des déclarations au "milieu" du code
    Oui mais tu ne fais plus du "C" mais du "Java"
    Mais encore une fois c'est juste une remarque

    Citation Envoyé par LittleT Voir le message
    J'ai en effet mal géré les exceptions, en pensant les rendre plus claires j'ai masqué certaines infos. Au début c'était surtout pour savoir quelle fonction pouvait lever tel ou tel type d'exception, plutôt que de mettre throws IOException etc. que je trouve assez obscur.
    La plupart du temps, lorsqu'on ne sait pas gérer une exception, il est plus simple (et plus propre) de la laisser remonter telle quelle...

    Citation Envoyé par LittleT Voir le message
    Je me suis aussi posé la question de la fermeture du reader dans la fonction elle même, toutefois étant donné qu'on le lit jusqu'à la fin, il aurait fallu qu'il ait été marqué auparavant avec une limite aléatoire (ce que j'ai jugé peu probable).
    C'est juste un choix personnel.
    Quand tu lis un code comme celui-là :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    InputStream input = ...;
    try {
        method(input);
    } finally {
       input.close();
    }
    Tu vois qu'un ressource est ouverte, utilisée dans une méthode, puis refermée.



    A l'inverse lorsque tu vois ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    InputStream input = ...;
    method(input);
    Il faut connaitre le fonctionnement de "method()" pour savoir si le flux est fermé ou pas, et perso je ne trouve pas cela super car c'est source d'erreur...

    Cela peut sembler anodin, mais sur une application serveur destinée à tourner sans interruption, le fait de ne pas libérer une ressource peut avoir de fâcheuses répercutions...




    Citation Envoyé par LittleT Voir le message
    Comme on peut le voir, j'ai effectué des tests et lorsque j'ai un pas trop petit (fonction readAll()), la copie dure trop longtemps.
    En fait il faudrait surtout savoir ce que tu veux faire.
    Vis à vis de ton premier message, j'étais partis dans l'idée que tu voulais lire un fichier en mémoire (pour le manipuler par exemple).

    Mais si tu veux faire une copie cela ne s'applique pas.

    Citation Envoyé par LittleT Voir le message
    Exemple : pour copier un fichier d'environ 40 Méga Octets avec un pas de 8 Kilo Octets (8 * 1024 * taille d'un byte, qui est comme tu l'as dis adiGuba la taille standart pour un buffer de BufferedReader), le copie était tellement longue que j'ai du arrêter avant la fin ( + d'une minute) et en plus la place occupée par le programme en RAM augmentait (dans les 400Mo vers la fin je crois).
    C'est normal car tu augmentes le buffer par "pas", or ce n'est pas ce qu'il faut faire dans ce genre de cas.

    En effet 40 Méga c'est 40000000 octets. Si tu augmentes ton buffer par pas de 8192 il va te falloir près 4883 augmentation du buffer. Ce qui signifie que tu va créer 4883 tableau de byte[] pendant ton traitement. Bref tu va passer plus de temps à allouer/désallouer de la mémoire qu'autre chose...

    Citation Envoyé par LittleT Voir le message
    Toutefois, ça me gène d'allouer directement un buffer de 4Mo (1024 * 1024 * 4 * taille d'un byte) comme dans le code ici, je trouve ça assez brutal comme méthode. Toutefois, avec un tel buffer, je met environ entre 500ms et 1s pour copier les 40 Méga Octets et moins de 500ms lorsque je prend un buffer de taille supérieure à la taille du fichier. Je trouve aussi que c'est assez moche de faire de multiples appels à Arrays.copyOf(), mais ça à l'air de fonctionner parfaitement.
    Si vous avez d'autres méthodes plus propres mais tout autant fonctionnelles, je prend.
    En fait dans ces cas là en général on double la taille du buffer final à chaque fois qu'on n'a plus de place. Ainsi en partant d'une taille de 8192 il suffit seulement de 14 opérations pour obtenir un buffer suffisamment grand.
    Ou mieux on peut directement déclarer le buffer à la bonne taille si on la connait (ce qui est le cas avec des fichiers).

    Bref ceci devrait donner de bien meilleurs résultats :
    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
    	public static byte[] readAll(InputStream reader) throws IOException,
    			NullPointerException, NegativeArraySizeException {
    		if (reader == null)
    			throw new NullPointerException("'InputStream reader' is null.");
     
    		int len = 0, tailleLue = 0;
    		byte[] buffer = new byte[1024 * 8];
    		long debut = System.currentTimeMillis(), fin;
     
    		while ((len = reader.read(buffer, tailleLue, buffer.length - tailleLue)) > 0) {
    			tailleLue += len;
    			// S'il n'y a plus de place dans le buffer :
    			if (tailleLue == buffer.length) {
    				// On double la taille du buffer
    				buffer = Arrays.copyOf(buffer, buffer.length * 2);
    			}
    		}
     
    		fin = System.currentTimeMillis();
    		System.out.println(fin - debut);
     
    		return Arrays.copyOf(buffer, tailleLue);
    	}


    Citation Envoyé par LittleT Voir le message
    N'hésitez pas non plus à critiquer tout le code comme vous l'avez fait si quelque chose vous gêne encore, je suis là aussi preneur
    Puisque tu demandes

    Perso je déconseille d'utiliser ce genre de structure :
    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
    		InputStream is = null;
    		OutputStream os = null;
    		try {
     
    			is = new FileInputStream(new File("entree"));
    			os = new FileOutputStream(new File("sortie"));
     
    			// traitmements...
     
    		} catch (Exception e) {
    			e.printStackTrace();
    		} finally {
    			try {
    				is.close();
    				os.close();
    				System.out.println("Fermeture OK");
    			} catch (Exception e) {
    				e.printStackTrace();
    			}
    		}
    Non seulement tu as une double gestion des exceptions, mais tu as des cas qui peuvent engendrer d'autre erreur. Par exemple si tu as une exception à l'ouverture d'un flux, tu obtiendras un NullPointerException lorsque tu essayeras de le fermer (et tu perds au passage l'exception d'origine).

    Perso je conseille plutôt d'utiliser un try/finally par ressource, avec éventuellement un try/catch qui englobe le tout si on a besoin de traiter les exception :
    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
    		try {
    			InputStream is = new FileInputStream("entree");
    			try {
    				OutputStream os = new FileOutputStream("sortie");
    				try {
    					// traitements...
    				} finally {
    					os.close();
    				}
    			} finally {
    				is.close();
    			}
    		} catch (IOException e) {
    			e.printStackTrace();
    		}
    Ou bien à partir de Java 7 le try-with-ressources qui gère tout cela proprement :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    		try (InputStream is = new FileInputStream("entree");
    			OutputStream os = new FileOutputStream("sortie")) {
    			// traitements
    		} catch (IOException e) {
    			e.printStackTrace();
    		}

    Citation Envoyé par LittleT Voir le message
    EDIT : En plus ce que je n'aime pas dans ma méthode, c'est que la totalité de ce qui est à copier se trouve à un moment dans la RAM. Pour des fichiers de plus d'1 GO ça commence à poser problème..
    Après ton premier message, j'étais partis dans l'idée que tu voulais charger le fichier en mémoire justement.

    Mais si tu veux "seulement" faire une copie, alors il est bien plus utile de faire la copie à la volée entre les deux flux :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    	public static void copy(InputStream input, OutputStream output,
    			int bufferSize) throws IOException {
    		byte[] buf = new byte[bufferSize];
    		int len;
     
    		while ((len = input.read(buf)) > 0) {
    			output.write(buf, 0, len);
    		}
    		output.flush();
    	}
    Du coup pour copier un fichier dans l'autre tu peux utiliser ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    public static void copy(File inputFile, File outputFile) throws IOException {
    		FileInputStream input = new FileInputStream(inputFile);
    		try {
    			FileOutputStream output = new FileOutputStream(outputFile);
    			try {
    				copy(input, output, 8192);
    			} finally {
    				output.close();
    			}
    		} finally {
    			input.close();
    		}
    	}
    Et bien sûr tu peux mixer fichiers, socket voir d'autre type de flux...


    a++

  6. #6
    Expert éminent sénior
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 481
    Points : 48 806
    Points
    48 806
    Par défaut
    Citation Envoyé par adiGuba Voir le message


    En fait dans ces cas là en général on double la taille du buffer final à chaque fois qu'on n'a plus de place. Ainsi en partant d'une taille de 8192 il suffit seulement de 14 opérations pour obtenir un buffer suffisamment grand.
    Il est quand même préférable le plus possible:
    => D'éviter d'avoir besoin de garder le fichier en mémoire quand tu sais déjà à l'avance qu'il dépassera une taille de quelques Ko. Aujourd'hui c'est 40M et demain ce sera combien? 200M? 1G? 20G?
    => Si c'est inévitable, d'avoir directement un buffer à la bonne taille. En plus des copies, on triple parfois la mémoire nécessaire avec le principe du doublage de taille. Un agrandissement de tableau se fait pas la création d'un nouveau tableau, plus grand et le transvasement des données. Donc dans le pire des cas, pour lire un fichier de 40Mo, il faudra 120Mo. On crée un tableau de 39.9M, il est plein, donc on en crée un nouveau de 79.8M, on y copie les donnée et on y met enfin l'octet restant. Ensuite seulement on supprime le tableau de 39.9M.

  7. #7
    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 tchize_ Voir le message
    Il est quand même préférable le plus possible:
    => D'éviter d'avoir besoin de garder le fichier en mémoire quand tu sais déjà à l'avance qu'il dépassera une taille de quelques Ko. Aujourd'hui c'est 40M et demain ce sera combien? 200M? 1G? 20G?
    Oui tout à fait.
    Mais après ca dépend aussi de l'objectif de ton application...
    Citation Envoyé par tchize_ Voir le message
    => Si c'est inévitable, d'avoir directement un buffer à la bonne taille. En plus des copies, on triple parfois la mémoire nécessaire avec le principe du doublage de taille. Un agrandissement de tableau se fait pas la création d'un nouveau tableau, plus grand et le transvasement des données. Donc dans le pire des cas, pour lire un fichier de 40Mo, il faudra 120Mo. On crée un tableau de 39.9M, il est plein, donc on en crée un nouveau de 79.8M, on y copie les donnée et on y met enfin l'octet restant. Ensuite seulement on supprime le tableau de 39.9M.
    Oui mais il faut connaitre cette taille, ce qui n'est pas forcément toujours le cas...

    Et dans le cas où on ne la connait pas, il faut passer par ce choix de doubler le buffer. Sinon on risque de consommer encore plus de mémoire...


    a++

  8. #8
    Futur Membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Mars 2012
    Messages
    6
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mars 2012
    Messages : 6
    Points : 6
    Points
    6
    Par défaut
    Merci beaucoup à tous les deux et désolé pour mon absence, je ne pouvais pas me connecter pendant tout ce temps. Vos conseils m'ont bien aidé et mon problème est parfaitement résolu .

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Lecture/Ecriture de fichiers MAT via .NET
    Par mimic50 dans le forum MATLAB
    Réponses: 3
    Dernier message: 29/03/2007, 12h48
  2. lecture/ecriture de fichier à distance avec indentification
    Par Mat1664 dans le forum Entrée/Sortie
    Réponses: 3
    Dernier message: 22/05/2006, 18h16
  3. C -> Perl : Lecture ecriture sur des Sockets
    Par caesarvanou dans le forum Programmation et administration système
    Réponses: 2
    Dernier message: 05/05/2006, 12h20
  4. Réponses: 4
    Dernier message: 03/02/2006, 14h50
  5. lecture-ecriture de fichier en mode Random (Get - Put)
    Par MuShRo_Om dans le forum VB 6 et antérieur
    Réponses: 9
    Dernier message: 15/01/2006, 15h53

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