Exploration du Nouveau Système d'Entrées/Sorties (NIO) en Java

Le package java.nio en Java offre une approche plus performante pour la gestion des entrées/sorties, particulièrement adaptée au traitement de gros volumes de données. Contrairement au modèle traditionnel basé sur les flux, NIO utilise des canaux (Channels) et des tampons (Buffers) pour un accès plus direct et efficace aux données.

Manipulation de Fichiers avec les Canaux

Les canaux permettent d'établir des connexions aux sources de données et d'y écrire ou en lire. Voici un exemple de création et de modification d'un fichier à l'aide de canaux :


package com.java.nio.operations;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class FileChannelOps {
   private static final int BUFFER_SIZE = 1024;

   public static void main(String[] args) throws Exception {
       // Créer un canal pour écrire dans un fichier
       try (FileChannel writeChannel = new FileOutputStream("output.txt").getChannel()) {
           writeChannel.write(ByteBuffer.wrap("Contenu initial ".getBytes()));
       }

       // Utiliser RandomAccessFile pour ajouter du contenu à la fin
       try (RandomAccessFile raf = new RandomAccessFile("output.txt", "rw");
            FileChannel rwChannel = raf.getChannel()) {
           rwChannel.position(rwChannel.size()); // Se positionner à la fin du fichier
           rwChannel.write(ByteBuffer.wrap("Ajout supplémentaire.".getBytes()));
       }

       // Lire le contenu du fichier
       try (FileChannel readChannel = new FileInputStream("output.txt").getChannel()) {
           ByteBuffer buffer = ByteBuffer.allocate(BUFFER_SIZE);
           readChannel.read(buffer);
           buffer.flip(); // Préparer le buffer pour la lecture

           while (buffer.hasRemaining()) {
               System.out.print((char) buffer.get());
           }
           System.out.println();
       }
   }
}
   

Lors de l'utilisatino de FileChannel pour la lecture, il est crucial d'appeler buffer.flip() après une opération de lecture pour préparer le tampon à être lu. Si des opérations de lecture successives sont prévues, buffer.clear() doit être appelé avant chaque nouvelle lecture pour réinitialiser le tampon.

Copie de Fichiers Efficace

NIO propose des méthodes optimisées pour transférer des données directement entre canaux, sans passer nécessairement par des tampons intermédiaires explicites. Les méthodes transferTo() et transferFrom() sont idéales pour cela.


package com.java.nio.operations;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.channels.FileChannel;

public class EfficientFileCopy {
   public static void main(String[] args) throws Exception {
       if (args.length != 2) {
           System.err.println("Usage: java EfficientFileCopy <source> <destination>");
           System.exit(1);
       }

       try (FileChannel sourceChannel = new FileInputStream(args[0]).getChannel();
            FileChannel destinationChannel = new FileOutputStream(args[1]).getChannel()) {

           // Transfère directement les données du canal source vers le canal destination
           long transferred = sourceChannel.transferTo(0, sourceChannel.size(), destinationChannel);
           System.out.println("Nombre d'octets transférés : " + transferred);

           // Alternativement :
           // destinationChannel.transferFrom(sourceChannel, 0, sourceChannel.size());
       }
   }
}
   </destination></source>

Cette approche est généralement plus performante que la copie octet par octet via un tampon.

Gestion des Caractères et Encodages

Bien que les tampons NIO travaillent avec des octets, il est souvent nécessaire de les interpréter comme des caractères. La conversion directe d'un ByteBuffer en CharBuffer peut être délicate en raison des différences d'encodage.


package com.java.nio.operations;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;

public class BufferTextHandling {
   private static final int BUFFER_SIZE = 1024;

   public static void main(String[] args) throws Exception {
       // Écrire des données initiales
       try (FileChannel fc = new FileOutputStream("data_text.txt").getChannel()) {
           fc.write(ByteBuffer.wrap("Texte initial".getBytes()));
       }

       // Lire et décoder
       try (FileChannel fc = new FileInputStream("data_text.txt").getChannel()) {
           ByteBuffer byteBuffer = ByteBuffer.allocate(BUFFER_SIZE);
           fc.read(byteBuffer);
           byteBuffer.flip();

           // Tentative d'affichage direct (peut ne pas fonctionner comme attendu)
           // System.out.println("Affichage direct CharBuffer: " + byteBuffer.asCharBuffer());

           // Décoder avec l'encodage par défaut du système
           String encoding = System.getProperty("file.encoding");
           System.out.println("Décodé avec " + encoding + ": " + Charset.forName(encoding).decode(byteBuffer));

           // Écrire en utilisant un encodage spécifique
           byteBuffer.clear(); // Réinitialiser pour réutiliser
           try (FileChannel writeChannel = new FileOutputStream("data_text.txt").getChannel()) {
               writeChannel.write(ByteBuffer.wrap("Texte encodé UTF-16BE".getBytes("UTF-16BE")));
           }

           // Relire et afficher avec l'encodage UTF-16BE
           byteBuffer.clear();
           try (FileChannel readChannel = new FileInputStream("data_text.txt").getChannel()) {
                readChannel.read(byteBuffer);
                byteBuffer.flip();
                System.out.println("Affichage UTF-16BE: " + byteBuffer.asCharBuffer());
           }

           // Écrire en utilisant CharBuffer directement
           byteBuffer.clear();
           CharBuffer charBuffer = byteBuffer.asCharBuffer();
           charBuffer.put("Utilisation de CharBuffer.");
           try (FileChannel writeChannel = new FileOutputStream("data_text.txt").getChannel()) {
               // Rembobiner le CharBuffer pour qu'il soit prêt à être lu par le ByteBuffer
               charBuffer.flip();
               writeChannel.write(byteBuffer);
           }

           // Relire et afficher le contenu écrit via CharBuffer
           byteBuffer.clear();
           try (FileChannel readChannel = new FileInputStream("data_text.txt").getChannel()) {
               readChannel.read(byteBuffer);
               byteBuffer.flip();
               System.out.println("Affichage via CharBuffer: " + byteBuffer.asCharBuffer());
           }
       }
   }
}
   

Pour une gestion corrcete des caractères, il est recommandé d'utiliser explicitement la classe Charset pour encoder et décoder les données, ou d'employer CharBuffer pour les opérations spécifiques aux caractères.

Étiquettes: Java NIO filechannel bytebuffer Charset

Publié le 16 juillet à 19h56