• Aucun résultat trouvé

Création, enregistrement et lecture d’un fichier binaire

Comme illustré précédemment, la gestion séquentielle de données de type complexe en mémoire de masse utilise des fichiers binaires. Ils sont également utilisés pour assurer la persistance des objets. L’exemple précédent est suffisamment illustratif pour ce qui concerne l’enregistrement de structures. Mais voici un exemple tout différent qui rappellera peut être à certains, d’anciens utilitaires de visualisations et de modifications d’exécutables.

Le but du programme suivant est d’afficher, en hexadécimal et en ASCII, le contenu d’un programme exécutable et de permettre le remplacement d’une chaîne par une autre de même longueur. Il est donné à titre d’exemple et sans aucune prétention de performance.

Le programme cible de l’expérimentation est un programme en langage C qui est compilé et qui affiche Bonjour en session Dos. Le fichier exécutable est Bonjour.Exe et il a une taille de ±19 Ko. Voici son code source en C :

#include <stdio.h> main() { printf("\nBonjour"); getc(stdin); return 0; }

L’interface présente deux ListBox (LBHexa et LBAscii) pour l’affichage du contenu du fichier, deux TextBox

(TCherche et TRemplace) pour l’acquisition de la chaîne cherchée et de celle de remplacement, et un jeu de boutons qui permet le pilotage du programme :

BCharger provoque la lecture du fichier et son affichage dans les ListBox BRemplacer remplace la chaîne dans les ListBox

Voici le code complet du programme.

Imports System.IO

Imports System.Text.RegularExpressions ' Outils de manipulation des chaînes

Public Class FBase

Private Sub BCharger_Click(ByVal sender As Object, ByVal e As System.EventArgs)

Handles BCharger.Click Dim ValeurLue(2000) As Byte ' Un buffer de lecture de 2000 octets

Dim UneLigneHex As String = String.Empty ' La ligne à placer dans LBHexa

Dim UneLigneAsc As String = String.Empty ' La ligne à placer dans LBAscii

Dim i As Integer

Dim FichierEntree As String = "X:\Tmp\Bonjour.Exe" Dim FEntree As BinaryReader

FEntree = New BinaryReader(File.Open(FichierEntree, FileMode.Open, FileAccess.Read)) LBHexa.Items.Clear()

LBAscii.Items.Clear() ' Effectuer la lecture jusqu’à EOF par lots

While FEntree.PeekChar() <> -1 ' de maximum 2000 octets et constituer les

ValeurLue = FEntree.ReadBytes(2000) ' lignes des ListBox en les limitant à 16

For i = 1 To ValeurLue.Length ' octets chacune pour faciliter la lecture

UneLigneHex = UneLigneHex & String.Format("{0:X2}", ValeurLue(i - 1)) UneLigneAsc = UneLigneAsc & Char.ConvertFromUtf32(ValeurLue(i - 1)) If i Mod 16 = 0 Then

LBHexa.Items.Add(UneLigneHex)

UneLigneHex = String.Empty ' Vider la ligne sauvée

LBAscii.Items.Add(UneLigneAsc) UneLigneAsc = String.Empty End If Next End While FEntree.Close() End Sub

Private Sub BRemplacer_Click(ByVal sender As Object, ByVal e As System.EventArgs)

Handles BRemplacer.Click Dim RechAsc, RempAsc As String

Dim RechHex As String = String.Empty Dim RempHex As String = String.Empty Dim Ligne As String

Dim N As Integer Dim SRech As Regex

RechAsc = TCherche.Text ' Acquisition des chaînes en ASCII

RempAsc = TRemplace.Text

For N = 0 To RechAsc.Length – 1 ' Création des chaînes en hexadécimal

RechHex = RechHex & String.Format("{0:X2}", RechAsc.Substring(N, 1)) RempHex = RempHex & String.Format("{0:X2}", RechAsc.Substring(N, 1)) Next

SRech = New Regex(RechAsc) ' La recherche s’effectue par la chaîne ASCII

For N = 0 To LBAscii.Items.Count – 1 ' pour chaque ligne de la liste LBAscii, et

Ligne = LBAscii.Items.Item(N) ' comme les 2 listes présentent les mêmes

If SRech.IsMatch(Ligne) Then ' caractères aux mêmes endroits, exception Ligne = Ligne.Replace(RechAsc, RempAsc) ' faite du format, si une ligne de LBAscii

LBAscii.Items.RemoveAt(N) ' contient la chaîne cherchée, la ligne

LBAscii.Items.Insert(N, Ligne) ' correspondante de LBHexa la contient aussi.

LBAscii.SelectedIndex = N ' La chaîne trouvée dans une ligne d’une

Ligne = LBHexa.Items.Item(N) ' liste est donc remplacée dans deux lignes.

Ligne = Ligne.Replace(RechHex, RempHex) ' Chacune remplace ensuite sa ligne initiale

LBHexa.Items.RemoveAt(N) ' dans sa liste d’origine.

LBHexa.Items.Insert(N, Ligne) LBHexa.SelectedIndex = N

Exit For ' On termine si on a trouvé. Le programme ne

End If ' remplace donc que la première occurrence

Next ' éventuellement trouvée.

Private Sub BEnregistrer_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles BEnregistrer.Click Dim UnByte As Byte

Dim NrLigne As Integer Dim i As Byte

Dim FichierSortie As String = "X:\Tmp\Bonjour.Exe" Dim FSortie As BinaryWriter

FSortie = New BinaryWriter(File.Open(FichierSortie, FileMode.Open, FileAccess.Write)) For NrLigne = 0 To LBAscii.Items.Count - 1

For i = 0 To LBAscii.Items.Item(NrLigne).Length - 1

UnByte = Char.ConvertToUtf32(LBAscii.Items.Item(NrLigne).Substring(i, 1), 0) FSortie.Write(UnByte) ' Chaque caractère est converti en sa valeur

Next ' numérique Ascii et enregistré

Next

FSortie.Close() End Sub

End Class

L’enregistrement octet par octet est très peu performant. Il serait plus rentable de traiter un important nombre d’octets en mémoire et d’en commander l’enregistrement par lots, à l’instar de ce qui est fait pour la lecture du fichier.

Et ce n’est pas la seule lacune de ce programme. En effet, la recherche de la chaîne à remplacer s’effectue à l’intérieur de chaque ligne de seize caractères de la ListBox et par conséquent, si cette chaîne ne se trouve pas entièrement sur une même ligne, elle n’est pas trouvée. Enfin, seule la première occurrence trouvée est remplacée. Le but n’était pas de fournir un réel utilitaire de bidouillage, mais seulement un autre exemple de traitement binaire d’un fichier.

A propos des outils utilisés

ConvertFromUtf32 La méthode ConvertFromUtf32 de la classe Char donne le caractère dont la valeur Ascii lui est passée en paramètre. La méthode ConvertToUtf32(Chaîne, Index_du_caractère)

réalise la convertion inverse.

Format La méthode Format de la classe String sert, comme son nom l’indique, à la mise en forme de diverses valeurs. Elle est utilisée dans le programme précédent pour représenter les valeurs Ascii des caractères sous forme de paires de chiffres hexadécimaux :

String.Format("{0:X2}", RechAsc.Substring(N, 1)). Les possibilités de formatage sont nombreuses. Plusieurs sont étudiées dans une autre partie de ce cours. En attendant, le lecteur peut une fois de plus consulter l’aide de son VisualStudio.Net et le site de MicroSoft déjà référencé.

Substring Cette autre méthode de la classe String permet l’extraction d’un ou plusieurs caractères d’une chaîne à partir d’une position donnée de 0 à Length – 1. Dans la phrase de code ci-dessus,

Substring extrait 1 caractère de RechAsc à la Nième position.

Length Cette méthode, toujours de la classe String, fournit le nombre de caractères d’une chaîne. Elle a déjà été utilisée précédemment sur des chaînes contenues dans les composants, comme dans l’expression LBAscii.Items.Item(NrLigne).Length de la fonction

BEnregistrer_Click. Dans ce programme elle est aussi utilisée pour connaître le nombre de caractères d’une variable dans l’expression RechAsc.Length – 1 de la fonction

BRemplacer_Click.

IsMatch Cette méthode de la classe System.Text.RegularExpressions effectue la recherche d’une chaîne donnée à l’intérieur d’une autre et retourne True en cas de succès. Elle est utilisée dans le programme pour vérifier l’existence de la chaîne recherchée à l’intérieur d’une ligne de LBAscii, dont les lignes sont présentées une par une à la méthode jusqu’à réussite de la recherche, ou jusqu’à la fin de la liste. Les expressions régulières sont étudiées davantage dans la partie suivante du cours.