• Aucun résultat trouvé

Lire et écrire via des boîtes de dialogue Lire

Nous allons travailler avec des objets de type Stream ("stream" en anglais = "flux" en français), qui représentent des flux d'octets.

Ne confondez pas byte et bit (qui vient de "binary digit") : 1 octet = 8 bits (= 1 byte en général). Contrairement à ce que croient énormément de personnes, "octet" n'est pas la traduction française du mot anglais "byte", mais de "octet" : c'est le même mot dans les deux langues. Un byte est la plus petite unité d'adressage mémoire d'un ordinateur et il se trouve que cette unité correspond à un octet, c'est-à-dire huit bits ; cependant on pourrait décider qu'un byte vaut plus (ou moins) de huit bits. Par abus de langage, on fait très souvent la confusion ; l'important est d'en avoir conscience. cf. Wikipédia : Byte

Qu'est-ce qu'un flux ?

Un flux est une connexion entre différents entités ; il transporte les données qu'elles s'échangent. On peut écrire des données dans le flux ou lire celles qui circulent déjà. Ici nous allons effectuer une connexion entre notre programme et l'espace de stockage de notre ordinateur.

Pour lire un fichier en utilisant les boîtes de dialogue, vous allez avoir besoin d'un objet de type OpenFileDialog, ici nommé openFileDialog1 (ici le nom n'est pas fabuleux car il s'agit simplement d'un exemple ; dans un vrai programme, nommez-le en fonction de ce à quoi il sert).

Pour en créer un, prenez-le dans la boîte à outils et mettez-le sur votre fenêtre. Ce n'est pas un objet qui s'affiche dans la fenêtre, il va donc se mettre en-dessous de votre fenêtre dans le mode Design.

Comme d'habitude, je vous donne d'abord le code (qui est à mettre dans une méthode) et ensuite je vous explique tout :

Code : C#

string fileName; string fileContent;

// On interdit la sélection de plusieurs fichiers. openFileDialog1.Multiselect = false;

// On supprime le nom de fichier, qui ici vaut "openFileDialog1" (avant sélection d'un fichier).

openFileDialog1.FileName = string.Empty;

// On met des filtres pour les types de fichiers :

"Nom|*.extension|autreNom|*.autreExtension" (autant de filtres qu'on veut).

openFileDialog1.Filter = "Fichiers texte|*.txt|Tous les fichiers|*.*";

// Le filtre sélectionné : le 2e (là on ne commence pas à compter à 0).

openFileDialog1.FilterIndex = 2;

// On affiche le dernier dossier ouvert. openFileDialog1.RestoreDirectory = true; // Si l'utilisateur clique sur "Ouvrir"...

if (openFileDialog1.ShowDialog() == DialogResult.OK) {

try

{

// On récupère le nom du fichier. fileName = openFileDialog1.FileName; // On lit le fichier.

fileContent = File.ReadAllText(fileName); }

// En cas d'erreur...

{

MessageBox.Show("Une erreur est survenue lors de l'ouverture du fichier : {0}.", ex.Message);

} }

La classe File est située dans l'espace de noms System.IO, n'oubliez donc pas le using System.IO; en début de fichier.

Les quelques premières lignes ne devraient pas poser trop de problèmes : on modifie simplement quelques propriétés de la boîte de dialogue. C'est dans le if que ça devient intéressant. L'ordinateur évalue openFileDialog1.ShowDialog() == DialogResult.OK pour obtenir un booléen. En fait, tout se passe comme si == était une méthode prenant deux arguments en entrée et retournant un booléen ; on pourrait avoir ==(openFileDialog1.ShowDialog(), DialogResult.OK). Je pense qu'on comprend mieux ce qui se passe avec cette écriture. Donc nous étions en train de dire que nous voulions évaluer cette expression. Pour cela, il faut commencer par évaluer les arguments. DialogResult.OK est déjà une valeur, donc pas de soucis. Par contre ShowDialog est une méthode : il faut donc l'appeler et exécuter le code qu'elle contient pour obtenir un résultat qui servira pour le ==.

Que fait la méthode ShowDialog ?

ShowDialog affiche une boîte de dialogue contenant un explorateur de fichiers et qui attend que vous en choisissiez un ou plusieurs (ici on a interdit d'en choisir plusieurs). Elle retourne un objet de type DialogResult (une énumération) qui donne des informations sur ce qu'a fait l'utilisateur. Cet objet peut valoir DialogResult.OK si l'utilisateur a bien choisi un fichier et qu'il n'a, par exemple, pas annulé. C'est précisément ce que l'on teste dans le if : on vérifie qu'un fichier a été choisi avant d'essayer d'y accéder.

Récapitulons : dans l'ordre chronologique on commence par afficher la boîte de dialogue et on attend que l'utilisateur fasse

quelque chose : le programme est bloqué à la méthode ShowDialog. Ensuite on compare le résultat de cette méthode avec DialogResult.OK à l'aide de l'opérateur == : si l'utilisateur a choisi un fichier alors on exécute le code du bloc if. Continuons l'analyse : on récupère d'abord le nom du fichier choisi puis on lit le fameux fichier en utilisant la méthode

File.ReadAllText qui ouvre le fichier, le lit en entier, le ferme et retourne son contenu. En une seule ligne, on rassemble des actions que nous pouvons séparer.

Dans cet exemple j'ai utilisé la méthode File.ReadAllText. Comme son nom l'indique, on lit les données en faisant comme si c'était du texte ; à nous d'être sûrs que nous lisons du texte, sinon nous obtiendrons une suite de caractères sans aucun sens. Suivant ce que l'on veut faire avec le fichier, on peut utiliser d'autres méthodes ou classes. Très souvent, on utilise des objets de type Stream (ou des types qui en dérivent). Cette classe est aussi située dans l'espace de noms System.IO, il faut donc mettre using System.IO; en début de fichier. Voici comment on peut faire avec cette nouvelle classe : je remplace juste la ligne fileContent = File.ReadAllText(fileName); par :

Code : C#

Stream myStream = null; StreamReader reader = null;

try

{

// Ouverture

myStream = openFileDialog1.OpenFile();

reader = new StreamReader(myStream, Encoding.UTF8); // Lecture

fileContent = reader.ReadToEnd(); }

catch

{

Console.WriteLine("Something went wrong!"); }

finally

{

if (reader != null) { reader.Dispose(); } if (myStream != null) { myStream.Dispose(); } }

Ici on a séparé ouverture, lecture et fermeture. Vous pouvez utiliser le mot-clef using pour appeler automatiquement à la fin du bloc correspond la méthode Dispose de l'objet entre parenthèses :

Code : C# try

{

// Ouverture

using (Stream myStream = openFileDialog1.OpenFile()) {

using (StreamReader reader = new StreamReader(myStream, Encoding.UTF8))

{

// Lecture

fileContent = reader.ReadToEnd(); }

// La méthode Dispose de reader vient d'être appelée. }

// La méthode Dispose de myStream vient d'être appelée. }

catch

{

Console.WriteLine("Something went wrong!"); }

Dans ce cas, la fermeture est implicite.

Le mot-clef using remplace seulement try et finally, il ne remplace pas catch ! Si jamais une exception est lancée dans le bloc using et que vous ne l'interceptez pas avec un catch, votre programme va devoir s'arrêter. En revanche, l'intérêt du mot-clef using est que les flux seront fermés même en cas d'exception. Cependant, si vous ne gérez pas les exceptions et que vous laissez Windows s'en occuper, votre programme peut être arrêté avant que les flux soient fermés, ce qui n'est pas bon ; pour cela, on place en général un bloc using au sein d'un bloc try (sans oublier le catch !).

Il est très important de fermer un flux une fois que vous n'en avez plus besoin : cela libère de la mémoire et cela permet de ne pas monopoliser l'accès à la ressource en question (d'autres programmes en ont peut-être besoin).

L'objet auquel on applique using doit implémenter l'interface IDisposable. Pour savoir si c'est le cas, faites un clic-droit sur le type de l'objet, puis cliquez sur "Go To Definition". Cela vous emmène à la définition de la classe. Voici ce que cela donne avec le type Stream :

Informations concernant la classe Stream (tirées des métadonnées).

Vous voyez que la classe Stream implémente bien l'interface IDsiposable et propose bien une méthode Dispose. Voici le code complet de la deuxième version (je le répète : ce code est à mettre dans une méthode) :

Code : C#

string fileName; string fileContent;

// On interdit la sélection de plusieurs fichiers. openFileDialog1.Multiselect = false;

// On supprime le nom de fichier, qui ici vaut "openFileDialog1" (avant sélection d'un fichier).

openFileDialog1.FileName = string.Empty;

// On met des filtres pour les types de fichiers :

"Nom|*.extension|autreNom|*.autreExtension" (autant de filtres qu'on veut).

openFileDialog1.Filter = "Fichiers texte|*.txt|Tous les fichiers|*.*";

// Le filtre sélectionné : le 2e (là on ne commence pas à compter à 0).

openFileDialog1.FilterIndex = 2;

// On affiche le dernier dossier ouvert. openFileDialog1.RestoreDirectory = true; // Si l'utilisateur clique sur "Ouvrir"...

if (openFileDialog1.ShowDialog() == DialogResult.OK) {

try

{

// On récupère le nom du fichier. fileName = openFileDialog1.FileName;

Stream myStream = openFileDialog1.OpenFile();

if (myStream != null) {

using (myStream) {

using (StreamReader reader = new StreamReader(myStream, Encoding.UTF8)) { fileContent = reader.ReadToEnd(); } } } } // En cas d'erreur...

catch (Exception ex) {

MessageBox.Show("Une erreur est survenue lors de l'ouverture du fichier : {0}.", ex.Message);

} }

J'utilise ici l'encodage UTF-8, mais vous pouvez en choisir un autre dans la classe Encoding.

Si vous faites un clic-droit sur StreamReader et que vous cliquez ensuite sur "Go To Definition", vous aurez accès aux métadonnées, dans lesquelles vous pourrez lire :

Code : C#

// Summary:

// Implements a System.IO.TextReader that reads characters from a byte stream

// in a particular encoding. [Serializable]

[ComVisible(true)]

public class StreamReader : TextReader

Vous voyez que le commentaire précise que l'on lit des caractères, autrement dit du texte. Si nous voulions lire des données brutes, nous n'aurions pas besoin de StreamReader et nous pourrions nous contenter de l'objet myStream.

Écrire

Pour écrire dans un fichier, le principe va être à peu près le même car nous allons encore utiliser une boîte de dialogue. La différence est qu'au lieu d'utiliser un contrôle de type OpenFileDialog, nous allons utiliser SaveFileDialog.

Comme vous devez vous en douter, il vous suffit encore d'un simple glisser-déposer depuis la boîte à outils pour créer un objet de type SaveFileDialog ; le mien gardera le piètre nom saveFileDialog1.

Voici maintenant comment écrire la valeur de fileContent dans un fichier (le code suivant va dans une méthode) :

Code : C#

saveFileDialog1.Filter = "Fichiers texte|*.txt|Tous les fichiers|*.*"; saveFileDialog1.FilterIndex = 2; saveFileDialog1.RestoreDirectory = true; if (saveFileDialog1.ShowDialog() == DialogResult.OK) { try {

Stream myStream = saveFileDialog1.OpenFile(); if (myStream != null)

using (myStream) {

using (StreamWriter writer = new StreamWriter(myStream, Encoding.UTF8)) { writer.Write(fileContent); } } } }

catch (Exception ex) {

MessageBox.Show("Une erreur est survenue lors de l'écriture du fichier: {0}.", ex.Message);

} }

La seule différence notoire est que nous utilisons ici un StreamWriter au lieu d'un StreamReader et la méthode Write au lieu de ReadToEnd.

fileContent doit être déclaré et initialisé avant que vous essayiez d'écrire son contenu dans le fichier. Je ne l'ai pas fait ici car je ne vous ai donné que le morceau concernant l'accès au fichier.

Là encore, on s'intéresse à du texte et j'utilise l'encodage UTF-8 (vous pouvez en choisir un autre dans la classe Encoding). J'ai porté l'accent sur le traitement de fichiers texte, car c'est le plus facile. Sachez que vous pouvez bien évidemment stocker tout ce que vous voulez, du son, des images, des vidéos, des données...

Documents relatifs