Apprenez à programmer en VB .NET
Last updated on Monday, June 24, 2013
  • 4 semaines
  • Facile

Ce cours est visible gratuitement en ligne.

Paperback available in this course

Ce cours existe en eBook.

Got it!

Les fichiers - partie 1/2

Dans cette partie, nous allons commencer à travailler sur les fichiers. Vous allez pouvoir commencer à enregistrer des données pour les récupérer, même si le programme s'est fermé entre temps (ce qui n'était pas possible avec les variables). Tout cela va nous permettre de créer des fichiers de configuration, sauvegarder des textes, des images, des scores, que sais-je encore…

Votre imagination est la seule limite de la programmation.

Introduction sur les fichiers

Nous allons commencer par une rapide introduction sur les fichiers. Je suppose que pour nombre d'entre vous c'est un concept acquis, mais pour d'autres il va falloir quelques éclaircissements.

Amis Windowsiens, vous pouvez apercevoir dans votre poste de travail des lecteurs et des disques durs. Dans le(s) disque(s) dur(s), vous pouvez naviguer suivant un système d'arborescence : le disque dur contient des dossiers, ces dossiers contiennent des fichiers (récursif : les disques durs peuvent contenir des fichiers et les dossiers d'autres dossiers).

Résumons : le disque dur contient toutes vos données ; le dossier permet de gérer, organiser et hiérarchiser votre disque dur ; les fichiers quant à eux contiennent des données pures et dures.

Les fichiers : des 0 et des 1

Les fichiers contiennent des données donc. Ces données sont représentées (côté machine de votre PC) par des 0 et des 1 (des bits), le système binaire qu'ils appellent ça. :lol:
Nous, pauvres petits mortels ne comprendrions rien à ce langage propre à la machine, c'est pourquoi des codages ont été mis en place pour convertir ces groupements de 0 et de 1 (généralement groupés par 8, ce qui donne 8 bits, autrement appelé un octet).

Donc individuellement, vous vous apercevez que ces 0 et ces 1 ne sont pas reconnaissables, on ne peut rien en tirer. Mais une fois en groupe, ces petits bits peuvent être transcrits différemment en fonction du codage.

Exemples :

Octet en binaire

Nombre décimal

Nombre hexadécimal

01100011

99

63

10010010

146

92

Alors, les cases dans chaque ligne de ce tableau ont la même valeur, seulement le codage utilisé n'est pas le même.

Le nombre décimal résultant de ces 0 et ces 1, vous le connaissez, pour peu que vous soyez allés à l'école primaire. En revanche, j'ai été méchant, j'ai ajouté une colonne avec à l'intérieur un nombre hexadécimal.

Sans m'étendre sur le sujet, le système hexadécimal est de base 16 (où le décimal est de base 10), il a été inventé par des informaticiens principalement pour des informaticiens. Il permet de transcrire rapidement des nombres binaires (car un groupement de 4 chiffres en binaire correspond à un chiffre hexadécimal).

Mais pourquoi nous dis-tu tout ça ?

Ça vient, ça vient. Donc vous avez compris que les données sont stockées sous forme de 0 et de 1, que des codages existent pour les transcrire en quelque chose de compréhensible par un humain. Pour le moment on se retrouve avec des nombres. Maintenant découvrons comment ils deviennent des caractères grâce à la norme ASCII.

La norme ASCII

La norme ASCII est la norme de codage de caractères standardisée. Autrement dit on l'utilise désormais dans tous les systèmes d'exploitation. Ici, c'est un groupement de 8 bits qui est converti en un caractère grâce à une table ASCII.

Exemple : la première suite de bits du tableau plus haut (01100011) correspond au caractère « c ».

Bref, je ne vous demande pas d'apprendre la table ASCII par cœur, notre IDE se chargera d'effectuer les codages tout seul. Tout ça pour vous sensibiliser un peu quant à la taille de vos fichiers. Windows a l'habitude de noter les tailles en ko (kilooctets) pour les petits fichiers, jusqu'aux Mo (mégaoctets), voire aux Go (gigaoctets) : 1 ko = 1024 octets, 1 Mo = 1 048 576 octets et 1 Go = 1 073 741 824 octets.

Donc 1024 caractères équivaudront à 1 ko.

Le namespace IO

Je vous ai peut être fait peur avec mes notions se rapprochant de la machine, mais ne vous inquiétez pas, c'était un peu de culture générale.

Microsoft, au travers de son framework (qui est une bibliothèque contenant des centaines de classes, fonctions, objets) a développé tous les outils nécessaires pour travailler facilement sur les fichiers, comme le montre la figure suivante.

Microsoft a tout prévu pour travailler sur les fichiers
Microsoft a tout prévu pour travailler sur les fichiers

Ce namespace (un namespace est une sorte de dossier contenant des classes et fonctions spécifiques) est le namespace IO. Comme vous le voyez sur le schéma, ces namespaces permettent d'organiser le contenu du framework.

Pourquoi un nom comme ça ? Fichiers aurait été mieux…

Eh bien, IO correspond à « Input Output ». En français : « Entrée Sortie ».

Ce namespace ne va pas contenir que les fonctions et objets pour manipuler les fichiers, il va nous permettre également d'effectuer de la communication inter-processus, de la communication série et de la compression. Mais n'allons pas trop vite.

Donc ce namespace fait lui-même partie du namespace System (comme dans votre ordinateur, on a plusieurs niveaux de dossiers pour mieux classer vos fichiers, eh bien ici c'est pareil avec les namespaces et les objets et fonctions).

Et dans ce namespace se situe la classe FileStream qui va nous permettre de créer un objet de type FileStream et de le manipuler. La classe File quant à elle ne nous permettra pas de créer un objet, mais seulement de manipuler notre FileStream. Vous allez très vite comprendre.

Une petite information supplémentaire avant de passer à la pratique : nous allons devoir créer un objet et le manipuler ; pour cela nous allons découvrir un nouveau mot récurrent en programmation : New.

Notre premier fichier

Créez un nouveau projet et rendez-vous dans l'événement du form load (je ne vous explique plus la démarche, vous êtes grands), je vous attends. Donc créons notre objet et entrons-le dans une variable.

De quel type, ma variable ?

Très bonne question, on va créer un objet permettant de manipuler les fichiers, ce serait malpropre de l'insérer dans un Integer, voire un String… Eh bien, j'ai dit que nous allions créer un objet FileStream, pourquoi ne pas l'entrer dans une variable de type FileStream ?

J'ai donc créé mon objet et je l'ai entré dans une variable :

Public Class Form1

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        'Création d'un objet de type FileStream
        Dim MonFichier As IO.FileStream = New IO.FileStream("Zero.txt", IO.FileMode.OpenOrCreate)

    End Sub

End Class

Plein de choses à vous expliquer en une seule ligne, j'aime ça ! Vous reconnaissez tous le Dim MonFichier As IO.FileStream, une simple déclaration de variable de type FileStream.

Pourquoi IO. devant ?

Je l'ai expliqué, on est dans le namespace IO, il faut donc faire IO. avant de pouvoir accéder aux membres du namespace.

Bon, déjà une chose de faite, continuons : = New IO.FileStream permet d'assigner une valeur à notre variable dès la déclaration, ça aussi nous l'avons vu. Le mot-clé New, j'ai dit qu'il servait à créer un nouvel objet (ici de type FileStream).

Instanciation de notre premier objet

Oui, j'avais compris, mais pourquoi tu as mis des choses entre parenthèses, ce n'est pas une fonction quand même !

Eh bien pas exactement. Lorsque nous instancions un objet (le mot instanciation fait peur, mais il signifie juste que nous créons un nouvel objet grâce à New), la classe utilisée pour déclarer l'objet va faire appel à son constructeur. Le mot « constructeur » est spécifique à la POO, nous reviendrons dessus plus tard. Le fait est que ce constructeur est appelé et parfois ce constructeur nécessite des arguments.

Dans notre cas, le constructeur de FileStream accepte plein de « combinaisons » d'arguments possibles (ce que l'on appelle la surcharge, j'expliquerai aussi plus tard).

J'ai choisi les plus simples : le Pathdu fichier (en String) avec lequel nous allons travailler, et un argument qui va nous permettre de déterminer comment ouvrir ou créer le fichier (de type IO.FileMode).

Le Path

Je vais faire une rapide parenthèse sur le Path. Tout d'abord le mot « path » signifie le chemin du fichier.
Ce chemin (je préfère parler de Path) peut être de deux types :

  • Absolu : le chemin n'a pas de référence, mais n'est pas exportable (ex : C:\Windows\System32… est un chemin absolu) ;

  • Relatif : le chemin prend comme point de repère le dossier d'exécution de notre programme (ex : Zero.txt sera placé dans le même dossier que le programme que nous créons).

Il est donc préférable d'utiliser des chemins relatifs dans nos programmes, à moins que vous ne soyez certains de l'emplacement des fichiers que vous voulez manipuler.

FileMode

Dans notre cas, j'ai inscrit un Path relatif, le fichier Zero.txt sera créé s'il n'existe pas, sinon il sera ouvert. Et tout cela grâce à l'argument IO.FileMode.OpenOrCreate.

Cet argument peut prendre quelques autres valeurs :

Nom VB

Valeur

Description

FileMode.CreateNew

1

Crée le fichier spécifié, s'il existe déjà une erreur se produira.

FileMode.Create

2

Crée le fichier s'il n'existe pas. S'il existe, le remplace.

FileMode.Open

3

Ouvre un fichier existant, une erreur se produira s'il n'existe pas.

FileMode.OpenOrCreate

4

Ouvre un fichier existant, s'il n'existe pas ce dernier sera créé, puis ouvert.

FileMode.Truncate

5

Ouvre le fichier spécifié et le vide entièrement, la lecture de ce fichier n'est pas possible dans ce mode.

FileMode.Append

6

Ouvre le fichier spécifié et se place à sa fin.

Comme vous le voyez, l'argument que j'ai utilisé, FileMode.OpenOrCreate (aussi remplaçable par le chiffre 4), permet d'adapter notre programme. Imaginez en utilisant l'argument FileMode.CreateNew, le premier lancement du programme se déroulera bien, mais lors du second lancement une erreur se produira parce que le fichier existe déjà. À moins que vous ne gériez toutes ces éventualités. Mais nous sommes des Zéros, allons au plus simple.

Résumé

Résumons ce que cette instruction a fait : on a ouvert le fichier Zero.txt (créé s'il n'existait pas) et on l'a dans la variable MonFichier.

Nos premières manipulations

Programme de base

Bon continuons, créons une petite interface basique permettant de lire et écrire dans le fichier. J'aimerais donc que vous créiez quelque chose qui ressemble à la figure suivante.

Notre programme ressemblera à ça
Notre programme ressemblera à ça

Alors, pour ce qui est des noms des contrôles, je pense que vous êtes grands maintenant, ils ne vont plus poser problème.
Mes deux textbox (TXT_LECTURE, TXT_ECRITURE) ont la propriété Multilineà true, celle du haut a ReadOnlyà true.
Des boutons (BT_LIRE, BT_CLEARLIRE, BT_ECRIRE, BT_CLEARECRIRE et BT_CLEAR tout en bas) et une checkbox (CHK_DEBUT).

Voilà pour ce qui est du design. Pour le code je vais vous montrer le mien et on va détailler le tout. Attention, je reprends pas mal de concepts abordés avant, tout en en intégrant des nouveaux, accrochez-vous !

Imports System.IO

Public Class Form1

    Dim MonFichier As IO.FileStream

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        'Création d'un objet de type FileStream 
        MonFichier = New IO.FileStream("Zero.txt", IO.FileMode.OpenOrCreate)
    End Sub

    Private Sub Form1_FormClosing(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.FormClosing
        'Libère la mémoire
        MonFichier.Dispose()
    End Sub

#Region "Gestion des boutons"

    Private Sub BT_LIRE_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles BT_LIRE.Click

        If MonFichier.CanRead() Then
            'Crée un tableau de Byte
            Dim Contenu(1024) As Byte
            'Lit 1024 bytes et les entre dans le tableau
            MonFichier.Position = 0
            MonFichier.Read(Contenu, 0, 1024)
            'L'affiche
            Me.TXT_LECTURE.Text = ""
            For Each Lettre As Byte In Contenu
                Me.TXT_LECTURE.Text += Chr(Lettre)
            Next
        End If

    End Sub

    Private Sub BT_ECRIRE_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles BT_ECRIRE.Click
        If MonFichier.CanWrite Then
            Dim Contenu(1024) As Byte
            Dim Compteur As Integer = 0
            'Parcours la textbox
            For Each Lettre As Char In Me.TXT_ECRITURE.Text.ToCharArray
                'Convertit une lettre en sa valeur ASCII et l'entre dans compteur
                Contenu(Compteur) = Asc(Lettre)
                Compteur += 1
            Next
            'Écrit dans le fichier
            If Me.CHK_DEBUT.Checked Then
                MonFichier.Position = 0
            End If
            MonFichier.Write(Contenu, 0, Compteur)
        End If
    End Sub

    Private Sub BT_CLEARLIRE_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles BT_CLEARLIRE.Click
        Me.TXT_LECTURE.Text = ""
    End Sub

    Private Sub BT_CLEARECRIRE_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles BT_CLEARECRIRE.Click
        Me.TXT_ECRITURE.Text = ""
    End Sub

    Private Sub BT_CLEAR_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles BT_CLEAR.Click
        'Je ferme le fichier actuel
        MonFichier.Dispose()
        'Je le réouvre en écrasant ses données
        MonFichier = New IO.FileStream("Zero.txt", FileMode.Create)
    End Sub

#End Region

End Class

Explications

Bien, bien, vous voilà avec des codes de plus en plus complexes. Prenons le problème par étapes. Tout d'abord nous avons les boutons de vidage des textbox qui ne sont pas sorciers, une simple instruction pour remplacer leur contenu.

Alors commençons à étudier le voyage de notre fichier. Je déclare en variable globale le fichier, de façon à ce qu'il soit accessible dans toutes les fonctions. Lors du Load, j'ouvre mon fichier comme nous l'avons vu dans la partie d'avant.

Et, chose importante, j'ai réagi à l'événement FormClosing(traduisible par « fenêtre en cours de fermeture », à ne pas confondre avec FormClosed: « fenêtre fermée »). Lorsque cet événement se produit, je Dispose()le fichier.

La fonction Dispose() permet de vider les ressources mémoire que prenait le fichier. En résumé, cela le ferme.

Donc, fichier ouvert et chargé à l'ouverture du programme, fermé à la fermeture. Parfait !
Travaillons.

Nous arrivons aux deux boutons Lire et Ecrire.

L'écriture

Bien, commençons par l'écriture (on ne va pas lire avant d'avoir écrit :lol: ).

Private Sub BT_ECRIRE_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles BT_ECRIRE.Click
        If MonFichier.CanWrite Then
            Dim Contenu(1024) As Byte
            Dim Compteur As Integer = 0
            'Parcours la textbox
            For Each Lettre As Char In Me.TXT_ECRITURE.Text.ToCharArray
                'Convertit une lettre en sa valeur ASCII et l'entre dans compteur
                Contenu(Compteur) = Asc(Lettre)
                Compteur += 1
            Next
            'Écrit dans le fichier
            If Me.CHK_DEBUT.Checked Then
                MonFichier.Position = 0
            End If
            MonFichier.Write(Contenu, 0, Compteur)
        End If
    End Sub
  • Alors, première instruction, déjà une nouvelle chose : MonFichier.CanWrite.
    C'est une propriété de l'objet. Elle nous informe sur la possibilité d'écriture dans notre fichier. Si c'est true, c'est parfait, on continue (ces petites vérifications sont souvent inutiles, mais il ne coûte rien de les faire et elles peuvent parfois éviter des erreurs, pensez aussi à gérer les cas d'erreurs).

  • Je crée ensuite un tableau de Byte, 1025 cases (je prévois grand !). Sachant que chaque byte (je n'ai pas expliqué, mais un byte est aussi de 8 bits dans notre cas, soit… un octet, soit… un caractère !) peut contenir un caractère, nous avons une possibilité d'écriture de 1025 caractères.

  • Un petit compteur, il va nous servir après.

  • Puis un For Each grâce auquel je parcours tous les caractères contenus dans ma textbox : Me.TXT_ECRITURE.Text.ToCharArray. La fonction ToCharArray permet, comme son nom anglais l'indique, de convertir en tableau de Char. Pour chaque caractère donc, ce caractère est entré dans la variable Lettre.

  • Je rentre chaque lettre dans mon tableau de Byte. Mais attention, les Byte et les Char ne sont pas homogènes, il faut passer par une fonction qui va récupérer la valeur binaire de notre caractère (transformation ASCII => 8 Bits grâce à la fonction Asc()) de façon à pouvoir l'inscrire dans le Byte.

  • Vient ensuite l'incrémentation du compteur pour pouvoir écrire chaque caractère dans une case différente.

  • Ensuite, si la case est cochée on déplace le curseur au début du fichier. Je vais parler des curseurs juste après.

  • Puis on écrit le contenu de notre tableau en indiquant combien de Byte écrire (avec compteur).

Eh bien, je sais qu'il y a pas mal de notions d'un coup. Reprenez le tout lentement en essayant de comprendre chaque ligne individuellement.

Les curseurs

Petit aparté sur les curseurs.

Alors je viens de parler de curseur dans notre fichier, mais qu'est-ce que cela ?

Non, n'y pensez même pas, ce n'est pas un curseur de souris qui bouge dans notre fichier, mais c'est comparable :
Un curseur doit être représenté mentalement. C'est un petit charriot qui avance dans notre fichier. Lorsqu'on lui demande de lire ou d'écrire, ce petit charriot va se déplacer de caractère en caractère et l'écrire (ou le lire). Donc lorsqu'on lit un fichier entier, le curseur se retrouve tout à la fin. Si on ne lit que la moitié, à la moitié.

Regardez le schéma présenté à la figure suivante.

Explications concernant le charriot
Explications concernant le charriot

Bref, tout ça pour dire que ce petit charriot ne bouge pas tout seul si on ne lui en donne pas l'ordre. Si je lis mon fichier, le curseur va se retrouver à la fin, lors d'une écriture sans bouger le curseur, l'écriture s'effectuera au début.

Pareil pour la lecture, si le curseur est à la fin et qu'on demande une lecture, il n'y aura rien à lire. Donc la propriété Positionpermet de spécifier l'index de ce curseur. Ici je le replace au début à chaque fois (0).

Mais attention, si je reprends l'écriture au début, le curseur ne s'occupe pas de ce qu'il y a avant, lorsqu'il va rencontrer un caractère déjà écrit dans le fichier il va purement et simplement le remplacer.

Faites bien attention donc et représentez-vous mentalement le trajet du curseur dans votre fichier pendant votre programme.

La lecture

Reprenons l'événement qui s'effectue lors du clic sur le bouton Lire :

Private Sub BT_LIRE_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles BT_LIRE.Click

        If MonFichier.CanRead() Then
            'Crée un tableau de Byte
            Dim Contenu(1024) As Byte
            'Lit 1024 bytes et les entre dans le tableau
            MonFichier.Position = 0
            MonFichier.Read(Contenu, 0, 1024)
            'L'affiche
            Me.TXT_LECTURE.Text = ""
            For Each Lettre As Byte In Contenu
                Me.TXT_LECTURE.Text += Chr(Lettre)
            Next
        End If

    End Sub
  • Première ligne, le même principe que pour l'écriture, on effectue un petite vérification pour savoir si l'on peut effectuer notre lecture.

  • On crée un tableau de Byte (comme l'écriture : 1025 cases).

  • On place le curseur à la position 0 (début de mon fichier).

  • On lit sur 1024 bytes (si le curseur rencontre la fin du fichier, la lecture s'arrête), et on place ce qui a été lu dans le tableau de Byte déclaré juste avant.

  • Puis un traditionnel For Each afin de parcourir toutes les cases de ce tableau de Byte.

  • On effectue une conversion Numerique => Caractère (soit Byte => ASCII grâce à la fonction Chr()), sinon vous ne liriez que des chiffres dans votre résultat ! On place le tout dans la textbox (grâce à += on ajoute les caractères à la suite).

Eh bien voilà, cela nous donne la figure suivante en résultat de tests.

Une demande d'écriture dans notre fichier. Résultat dans le fichier : « Bonjour les Zéros ! »
Une demande d'écriture dans notre fichier. Résultat dans le fichier : « Bonjour les Zéros ! »
Une demande de lecture, le fichier n'a pas changé, son contenu est toujours le même
Une demande de lecture, le fichier n'a pas changé, son contenu est toujours le même
L'écriture d'une seconde ligne
L'écriture d'une seconde ligne

J'en profite pour vous dire que les caractères permettant de matérialiser le retour à la ligne sont contenus dans la chaîne que vous récupérez de votre textbox, donc lorsqu'on demande une écriture de tout son contenu, le caractère est également écrit dans le fichier, le retour à la ligne s'effectue donc également dans le fichier sans manipulations supplémentaires.

Déjà une bonne chose de faite, ne partez pas ! On va apprendre de nouvelles fonctions et manipulations sur nos nouveaux amis les fichiers dans la partie suivante.

  • On utilise le namespace IO pour effectuer des opérations d'entrée-sortie sur les fichiers.

  • Il faut bien penser au curseur qui avance en même temps que la lecture ou l'écriture dans un fichier.

  • On peut utiliser des formats absolus ou relatifs pour les chemins des fichiers. Les chemins absolus ne sont pas fiables pour un programme destiné à être mis sur d'autres postes.

Example of certificate of achievement
Example of certificate of achievement