Qu'est-ce que l'E/S ?
L'abréviation E/S (ou I/O en anglais) signifie Entrée/Sortie (Input/Output). Pour un système informatique, les périphériques comme le clavier, la clé USB, l'interface réseau, l'écran, les haut‑parleurs ou la caméra sont des dispositifs d'E/S.
En C#, l'E/S représente l'échange de données entre le programme et l'extérieur. Le programme a besoin de données pour les traiter ; l'E/S fournit la source de ces données et permet d'exporter les résultats.
L'architecture d'E/S en C# se divise en trois couches : les flux de stockage sous‑jacents, les flux décorateurs et les adaptateurs de flux. Contrairement à Java, C# ne fait pas de distinction entre flux de caractères et flux d'octets : tout est basé sur le concept de Stream, avec des classes Reader/Writer associées.
Objets E/S courants en C#
1. La classe de base Stream
System.IO.Stream est une classe abstraite qui fournit des méthodes standard pour transférer des octets (lecture, écriture, etc.) vers et depuis une source. Les classes qui doivent lire/écrire des octets à partir d'une source spécifique héritent de Stream.
FileStream: lit ou écrit des octets dans un fichier physique (.txt, .exe, .jpg, etc.).MemoryStream: lit ou écrit des octets en mémoire.BufferedStream: améliore les performances d'E/S en mettant en mémoire tampon.NetworkStream: lit ou écrit des octets sur un socket réseau.PipeStream: permet la communication entre processus via un tube.CryptoStream: lie un flux de données à une transformatino cryptographique.
FileStream
Exemple d'écriture et de lecture avec FileStream :
using System;
using System.IO;
class TestFileStream
{
static void Main()
{
string chemin = @"C:\temp\demo.txt";
using (FileStream fs = new FileStream(chemin, FileMode.OpenOrCreate))
{
// Écrire un octet (65 = 'A')
fs.WriteByte(65);
}
// Écrire une séquence (A à Z)
using (FileStream fs = new FileStream(chemin, FileMode.OpenOrCreate))
{
for (byte b = 65; b <= 90; b++)
fs.WriteByte(b);
}
// Lire et afficher tous les octets
using (FileStream fs = new FileStream(chemin, FileMode.Open))
{
int valeur;
while ((valeur = fs.ReadByte()) != -1)
Console.Write((char)valeur);
}
}
}
MemoryStream
Exemple d'écriture et de lecture dans un flux mémoire :
using System;
using System.IO;
using System.Text;
class DemoMemoryStream
{
static void Main()
{
MemoryStream mem = new MemoryStream();
Console.WriteLine("Capacité initiale : {0}", mem.Capacity);
Console.WriteLine("Longueur initiale : {0}", mem.Length);
UnicodeEncoding encodeur = new UnicodeEncoding();
byte[] donnees = encodeur.GetBytes("Nouvelles données");
for (int i = 1; i <= 3; i++)
{
Console.WriteLine("Écriture n°{0}", i);
mem.Write(donnees, 0, donnees.Length);
}
Console.WriteLine("Capacité après écriture : {0}", mem.Capacity);
Console.WriteLine("Longueur après écriture : {0}", mem.Length);
// Lecture
mem.Seek(0, SeekOrigin.Begin);
byte[] tampon = new byte[mem.Length];
mem.Read(tampon, 0, tampon.Length);
Console.WriteLine(encodeur.GetString(tampon));
mem.Close();
}
}
BufferedStream
Exemple d'utilisation d'un tampon pour améliorer les performances :
using System;
using System.IO;
using System.Text;
class BufferedDemo
{
static void AjouterTexte(BufferedStream bs, string valeur)
{
byte[] info = new UTF8Encoding().GetBytes(valeur);
bs.Write(info, 0, info.Length);
}
static void Main()
{
string chemin = @"C:\temp\buffer.txt";
if (File.Exists(chemin)) File.Delete(chemin);
using (BufferedStream bs = new BufferedStream(File.Create(chemin)))
{
AjouterTexte(bs, "Première ligne ");
AjouterTexte(bs, "123456789\r\n");
AjouterTexte(bs, "Seconde ligne\r\n");
AjouterTexte(bs, "Une autre ligne");
}
using (BufferedStream bs = new BufferedStream(File.OpenRead(chemin)))
{
byte[] tampon = new byte[bs.Length];
UTF8Encoding utf8 = new UTF8Encoding();
while (bs.Read(tampon, 0, tampon.Length) > 0)
Console.WriteLine(utf8.GetString(tampon));
}
}
}
NetworkStream
Exemple simplifié de serveur et client utilisant NetworkStream :
// Serveur : écoute et reçoit un fichier
using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
class ServeurImage
{
static TcpClient client;
static FileStream fluxFichier;
static int tailleTampon = 200;
static byte[] tampon = new byte[tailleTampon];
static NetworkStream fluxReseau;
static void Main()
{
TcpListener ecouteur = new TcpListener(IPAddress.Any, 8080);
ecouteur.Start();
Console.WriteLine("Serveur en attente...");
client = ecouteur.AcceptTcpClient();
Console.WriteLine("Client connecté.");
fluxReseau = client.GetStream();
fluxFichier = new FileStream(@"C:\temp\recu.jpg", FileMode.Create);
// Lecture asynchrone
fluxReseau.BeginRead(tampon, 0, tailleTampon, LectureAsyncCallback, null);
Console.ReadLine();
}
static void LectureAsyncCallback(IAsyncResult ar)
{
int nbLus = fluxReseau.EndRead(ar);
if (nbLus > 0)
{
fluxFichier.Write(tampon, 0, nbLus);
fluxReseau.BeginRead(tampon, 0, tailleTampon, LectureAsyncCallback, null);
}
else
{
fluxFichier.Close();
client.Close();
}
}
}
// Client : envoie une image
using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
class ClientImage
{
static void Main()
{
EnvoyerImage(@"C:\chemin\vers\image.jpg");
}
static void EnvoyerImage(string chemin)
{
if (!File.Exists(chemin)) return;
byte[] donnees = File.ReadAllBytes(chemin);
TcpClient client = new TcpClient();
client.Connect(IPAddress.Parse("127.0.0.1"), 8080);
using (NetworkStream ns = client.GetStream())
{
ns.Write(donnees, 0, donnees.Length);
}
}
}
PipeStream
Exemple de communication entre processus avec un tube anonyme :
using System;
using System.IO;
using System.IO.Pipes;
using System.Diagnostics;
class DemoPipe
{
static byte[] signature = { 9, 0, 9, 0 };
static void Main()
{
string[] args = Environment.GetCommandLineArgs();
if (args.Length < 2)
{
// Processus parent (serveur)
Process client = new Process();
client.StartInfo.FileName = Environment.CommandLine;
using (AnonymousPipeServerStream pipeServeur = new AnonymousPipeServerStream(PipeDirection.In, HandleInheritability.Inheritable))
{
client.StartInfo.Arguments = pipeServeur.GetClientHandleAsString();
client.StartInfo.UseShellExecute = false;
client.Start();
pipeServeur.DisposeLocalCopyOfClientHandle();
if (AttendreSignature(pipeServeur))
Console.WriteLine("Signature valide reçue.");
else
Console.WriteLine("Signature invalide.");
}
client.WaitForExit();
}
else
{
// Processus enfant (client)
using (PipeStream pipeClient = new AnonymousPipeClientStream(PipeDirection.Out, args[1]))
{
pipeClient.Write(signature, 0, signature.Length);
}
}
}
static bool AttendreSignature(PipeStream flux)
{
byte[] recu = new byte[signature.Length];
int len = flux.Read(recu, 0, signature.Length);
if (len != signature.Length) return false;
for (int i = 0; i < len; i++)
if (recu[i] != signature[i]) return false;
return true;
}
}
2. Classes File et Directory
File
Méthodes principales :
Create(string): crée ou écrase un fichier.CreateText(string): crée un fichier texte en UTF-8.Delete(string): supprime un fichier.Exists(string): vérifie l'existence d'un fichier.
Exemple :
using System;
using System.IO;
class DemoFile
{
static void Main()
{
string chemin = @"C:\temp\exemple.txt";
if (!File.Exists(chemin))
{
using (StreamWriter sw = File.CreateText(chemin))
{
sw.WriteLine("Bonjour");
sw.WriteLine("Bienvenue");
}
}
using (StreamReader sr = File.OpenText(chemin))
{
string ligne;
while ((ligne = sr.ReadLine()) != null)
Console.WriteLine(ligne);
}
}
}
Directory
Méthodes principales :
CreateDirectory(string)Delete(string, bool): supprime un répertoire et éventuellement son contenu.Exists(string)GetDirectories(string)GetFiles(string)Move(string, string)
Exemple :
using System;
using System.IO;
class DemoDirectory
{
static void Main()
{
string dossier = @"C:\temp\TestDir";
if (!Directory.Exists(dossier))
Directory.CreateDirectory(dossier);
if (Directory.Exists(dossier))
Console.WriteLine("Dossier créé avec succès.");
// Déplacement
string nouveauDossier = @"C:\temp\TestDirDeplace";
Directory.Move(dossier, nouveauDossier);
Console.WriteLine("Dossier déplacé.");
}
}
3. Classe Path
Utile pour manipuler les chemins de fichiers :
string chemin = @"C:\Users\Public\Documents\test.txt";
Console.WriteLine(Path.GetFileName(chemin)); // test.txt
Console.WriteLine(Path.GetFileNameWithoutExtension(chemin)); // test
Console.WriteLine(Path.GetExtension(chemin)); // .txt
Console.WriteLine(Path.GetDirectoryName(chemin)); // C:\Users\Public\Documents
Console.WriteLine(Path.Combine(@"C:\temp", "sous-dossier", "fichier.txt"));
4. StreamReader et StreamWriter (lecture/écriture rapide de fichiers texte)
Ces classes simplifient la gestion des fichiers texte :
using System;
using System.IO;
class DemoReaderWriter
{
static void Main()
{
string chemin = @"C:\temp\bienvenue.txt";
// Écriture
using (StreamWriter sw = new StreamWriter(chemin))
{
sw.WriteLine("Ligne 1 : Bonjour.");
sw.WriteLine("Ligne 2 : Comment allez-vous ?");
sw.Write("Ligne 3 : Fin.");
}
// Lecture
using (StreamReader sr = new StreamReader(chemin))
{
string ligne;
while ((ligne = sr.ReadLine()) != null)
Console.WriteLine(ligne);
}
}
}