LES FICHIERS. DESCRIPTIONS ET UTILISATION.
1° partie : les bases.


Comme beaucoup de choses, on utilise les fichiers sans bien savoir ce que c'est ni comment ils sont structurés. Que signifie l'extension, qu'est ce qu'un "type fichier" qu'est ce qu'un format de fichier ? Dans cette première partie s'adressant essentiellement aux débutants, je propose de construire en mode pas à pas un petit agenda téléphonique avec photo d'identité et commentaire.

Article lu   fois.

L'auteur

Site personnel

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

1. Préambule : Qu'est ce qu'un fichier ?

La notion de fichier vient de l'époque où des données concernant un individu ou un objet particulier, étaient conservées sur des fiches cartonnées comme une fiche médicale par exemple. Un fichier est une collection de fiches rangées dans un classeur. Avec l'avènement de l'informatique, le terme et l'idée de fiches a été conservé. Les données ne sont plus écrites sur une fiche cartonnée, mais sous une forme binaire sur un support magnétique (ou optique sur un CDROM).
Bien sur, la notion de fichier a énormément évoluée. Une fiche médicale représente bien une structure (ou enregistrement), alors qu'une lettre représente un simple fichier texte, une suite de caractères sans organisation particulière. Mais, pour une machine, le clavier est un fichier d'entrées et la sortie vers imprimante est un fichier de sortie.

Un fichier a donc deux aspects, un aspect logique (ce qu'il représente) et un aspect matériel, le support sur lequel il est enregistré. On parle aussi de fichier virtuel. Avec tous les termes qu'emploient les informaticiens, chacun allant de son jargon, les débutants ont de quoi se perdre. Je vais donc commencer par exposer de façon aussi simple que possible quels sont les différents supports utilisés par les fichiers.

2. Pas de fichier sans un support matériel.

Disque dur, disque souple, bande magnétique, mémoire volatile (RAM) ou clef USB, quelles différences ?

Pour avoir une existence réelle, un fichier doit être stocké sur un support matériel. Un disque dur, une disquette (disque souple) sont des supports magnétiques comme la bande. Les données sont enregistrées sous forme binaire (0-1) dans de minuscules aimants dont la polarité représente 0 ou 1. Mais il y a aussi les supports électroniques, les mémoire vives (RAM) et les mémoires genre clef USB, composées de transistors ayant un état passant ou un état bloquant représentant le 0 ou le 1. Ce qu'on appelle fichier virtuel est tout simplement un fichier stocké dans la mémoire vive de l'ordinateur. Son existence cesse quand on éteint l'ordinateur.

Autres types de supports de fichiers, le CDROM et le DVD. Là, c'est sous une forme optique que sont enregistrées les données.

Description simplifiée d'un disque dur ou d'un disque souple (disquette). C'est un disque magnétique formaté en un certain nombre de pistes parallèles. Chaque piste est découpée en secteurs pouvant contenir potentiellement 512 ou 1024 octets.
Description simplifiée d'un CDROM ou DVD. C'est un disque optique ne contenant qu'une seule piste en spirale. Un CDROM de 650 MO a une piste de 6 Km de long. La piste à une largeur de 0,6µm et l'espace entre deux spires est de 1,6µm.

Image non disponible



Je ne parlerais pas d'avantage de l'aspect matériel ni comment les données sont stockées sur les supports amovibles ou rotatifs, car les systèmes de contrôle de rotation, de validité de lecture et d'écritures sont particulièrement complexes. Il ne s'agit plus d'informatique, mais d'électronique et d'électromécanique.

Comment est organisé un disque dur ou un disque souple ?

3 Parties les composent. Le secteur de Boot, la table d'allocation de fichier et à la suite, l'espace devant contenir les données.
Le secteur de Boot est la partie qui permet d'identifier le disque lui-même et ses caractéristiques. Il peut en plus contenir un programme directement exécutable par le BIOS (BasicInputOutputSystem) de l'ordinateur, permettant le démarrage du système d'exploitation. La table d'allocation de fichier est en quelque sorte le catalogue des fichiers enregistrés sur le disque. Mais plus qu'un catalogue, c'est un répertoire.
Que contient ce répertoire ?

Image non disponible



Fondamentalement, un fichier n'est que l'enregistrement d'une suite d'octets sur un support. Son emplacement sur le disque, son nom, sa taille, ses attributs (System, caché, lecture seule etc..) sont enregistrés dans ce répertoire qu'est la Table d'Allocation de Fichiers FAT, ou NTFS pour New Table Fat System.
Sur un CDROM, c'est un fichier en début de piste qui tient lieu de table d'allocation de fichier.

Remarque : Le BIOS (BasicInputOutputSystem) ou "Système d'entrées/sorties de base" est un programme stocké en dur sur la carte mère de l'ordinateur. C'est ce programme qui détermine tous les périphériques matériels de la machine (dont le disque dur), et qui détecte le programme d'exécution dans le secteur de BOOT si il existe.

A propos de BOOT, en connaissez vous le sens ? Vous en trouverez la définition ici Seteur de BOOT.
Je ne vous en dis pas plus... Surprise.

3. Les différents formats de fichiers et leurs extensions.

Si vous cherchez dans la littérature exposant les fichiers, vous trouverez plusieurs définitions. Les uns parlent de deux genres de fichiers, les fichiers textes et les fichiers binaires. On parle de format, de fichier typé et de fichier non typé.. Nous verrons progressivement les sens de ces termes.

L'extension d'un fichier exemple ".txt" ou ".jpg" fait partie de son nom et permet de savoir quel type de fichier il représente parmis certains standards, ou bien pour spécifier un type particulier créé selon le besoin.

Il y a bien longtemps, quand on programmait sous DOS, on distinguait (par convention) deux types de fichiers, le fichier texte et le fichier binaire. En fait ceux qu'on appelait fichier texte, étaient tout fichier composé de caractères ASCII éditable en mode dit "texte" comme le fait le bloc-note de windows. Les autres étaient appelé binaire car leur utilisation nécessitaient un programme particulier. A priori, un fichier texte représente un texte du point de vue littéraire. Mais sa structure est si simple qu'on a trouvé bien commode de l'utiliser à d'autres fins que de rédiger du courrier, comme nous le verrons plus loin.

Le format d'un fichier est la manière dont sont organisées les données qu'il contient.

3-1. Fichier texte.

Un fichier texte est une suite de caractères (d'octets) terminée par un retour chariot (code ASCII 13) qui est délimiteur de fin de ligne. Lors de l'écriture la procédure WriteLn se charge d'ajouter un retour chariot à la fin de chaque chaîne. A la lecture, la procédure ReadLn lira tous les caractères jusqu'au prochain retour chariot, ce retour chariot n'est pas copié dans la chaîne réceptrice. Certains programmes signalent la fin d'un fichier texte par le code EOF (code ASCII 26) appelé aussi "control Z", dans ce cas ce caractère n'est pas placé dans la chaîne réceptrice.

3-2. Fichier binaire.

On appel généralement "fichier binaire", tous les types de fichiers différents du type texte. Même si ce fichier représente du texte comme un fichier Word, c'est un fichier binaire. Il contient un ensemble de codes mélangés au texte pour définir la police de caractères, la couleur, les marges ect..

3-3. Les fichiers de type texte ne portant pas l'extension ".txt".

Comme je l'ai dit plus haut, la structure d'un fichier texte est si simple qu'on a trouvé bien commode de l'utiliser à d'autres fins que de rédiger du courrier. Je donne ici trois exemples de fichier ayant la même structure (du point vue informatique) que le fichier texte mais utilisé de façon particulière. Les fichier CSV, INI et HTML.

3-3-1. Le fichier du type tableur genre CSV (avec programme d'exemple).

Le fichier CSV est très ancien. Il a été utilisé par les tableurs. Les données enregistrées sont définies comme des champs, chaque champ est séparé du suivant par un caractère particulier et réservé à cet usage, le point-virgule. A la lecture, il suffit de compter le nombre de point virgule dans la chaîne pour connaître le nombre de champs. Mais on le trouve avec d'autres caractères comme séparateur de champ, la virgule ou la tabulation (code ASCII 9).

 Voici un exemple de contenu d'un fichier CSV :   Chien; Labrador;5;Noir;500 C'est par l'extension CSV que l'on sait que chaque mot (séparé du suivant par un ;) représente une donnée particulière. Ici, un animal du genre canin, de race Labrador, de 5 ans d'âge, de couleur noir et pour un prix de 500 euros.

Remarquez que EXCEL reconnaît ce type de fichier.

Exemple d'utilisation d'un fichier CSV. Vous trouverez à cette adresse le code source utilisant un "TStringGrid" permettant de charger, d'éditer, et d'enregistrer un fichier CSV.Source Exemple_CSV

3-3-2. Le fichier INI .

Un fichier INI est en quelque sorte une banque de données regroupée en sections. C'est un fichier texte. Les sections sont identifiées par des balises. Les balises sont les deux caractères "[" et "]".

Voici un exemple de fichier INI.

for 16-bit app support

[drivers]
wave=mmdrv.dll
timer=timer.drv

[mci]
[ASR1516$3.0.2]
Driver=dspli302.dll
Address=444
[ASR1532s$3.0.2]
Driver=dspli302.dll
Address=514
[ASR1016$3.0.2]
Driver=dspli302.dll
Address=345
[ASR1032s$3.0.2]
Driver=dspli302.dll
Address=666
[driver32]
[386enh]
device=C:\WINNT\system\wemu387.386
woafont=app850.FON
EGA80WOA.FON=EGA80850.FON
EGA40WOA.FON=EGA40850.FON
CGA80WOA.FON=CGA80850.FON
CGA40WOA.FON=CGA40850.FON


Il existe de très bons tutoriaux sur le sujet et de bons exemples dans la FAQ DELPHI du site.

3-3-3. Le fichier HTML.

Un fichier HTML est comme le fichier INI, purement et simplement un fichier texte. C'est son extension ".htm" ou ".html" qui permet de savoir qu'il doit être traité de façon particulière.

Le fichier HTML est composé de textes (au sens littéraire) entouré de Balises (des mots réservés au langage HTML) qui les feront apparaître avec une taille, un style et une couleur définit dans la balise.

Un fichier HTML ne contient jamais d'image mais des liens vers des adresses WEB qui permettent le chargement des images par le navigateur.

Comme sa structure est la même que celle d'un simple fichier texte, un fichier HTML peut aisément être édité et modifié avec le bloc note de WINDOWS.

3-4. Qu'est ce que le "Rich text file" ou "fichier texte enrichit" portant l'extension ".rtf" ?

Voici un texte tel qu'il apparaît à l'écran.



AVERTISSEMENTS CONSEILLES
Ce programme de retouche de photos, fonctionne
sous toutes les versions de WINDOWS 95, 98, ME,
NT4, 2000 et XP. Je l'ai testé avec des acquisitions
depuis un scanner à 7200 dpi sur une diapositive. le
résultat était une image de 62 millions de pixels.
Si vous rencontrez un problème quelconque je vous
serais gré de m'en faire part, en m'envoyant un
courrier électronique à philippe.gormand@free.fr
Vous pouvez m'écrire directement depuis mon site
WEB "Envoyer un message"
URL -> http://philippe.gormand.free.fr/

Ce programme a été testé avec des images de plus
10 millions de pixels sur plusieurs ordinateurs
dont un PENTIUM I à 100 Mhz et avec 64 MOctets
de mémoire vive sous WINDOWS 95.

Pour fonctionner confortablement avec de grandes
images, un équipement de 128 Moctets de mémoire
est nécessaire.

Il reconnaît plus de 20 formats de fichier d'image
différent, mais ne peut enregistrer que 10, BMP
JPG,TGA etc...


Voici le contenu du fichier.

{\rtf1\ansi\ansicpg1252\deff0\deflang1036{\fonttbl{\f0\fmodern\fprq1 Courier New;}{\f1\fmodern\fprq1\fcharset0 Courier New;}{\f2\froman Times New Roman;}} {\colortbl ;\red255\green0\blue0;\red0\green0\blue0;\red0\green0\blue255;} {\*\generator Msftedit 5.41.15.1507;}\viewkind4\uc1\pard\f0\fs22\tab\tab\cf1\b AVERTISSEMENTS CONSEILLES\par \cf2\b0\par Ce programme de retouche de photos, fonctionne\par sous toutes les versions de WINDOWS 95, 98, ME, \par \f1 NT4, 2000 et XP. Je l'ai test\'e9 avec des acquisitions\par depuis un scanner \'e0 7200 dpi sur une diapositive. le\par r\'e9sultat \'e9tait une image de 62 millions de pixels.\par Si vous rencontrez un probl\'e8me quelconque je vous\f0\par \f1 serais gr\'e9 de m'en faire part, en m'envoyant un\f0\par \f1 courrier \'e9lectronique \'e0 \cf3\f0 philippe.gormand@free.fr\cf2\par \f1 Vous pouvez m'\'e9crire directement depuis mon site\f0\par WEB "Envoyer un message" \par \b URL ->\b0 \cf3 http://philippe.gormand.free.fr/\par \cf2\par \f1 Ce programme a \'e9t\'e9 test\'e9 avec des images de plus\f0\par 10 millions de pixels sur plusieurs ordinateurs \par \f1 dont un PENTIUM I \'e0 100 Mhz et avec 64 MOctets \f0\par \f1 de m\'e9moire vive sous WINDOWS 95.\f0\par \par Pour fonctionner confortablement avec de grandes\par \f1 images, un \'e9quipement de 128 Moctets de m\'e9moire \f0\par \f1 est n\'e9cessaire.\f0\par \par \f1 Il reconna\'eet plus de 20 formats de fichier d'image\f0\par \f1 diff\'e9rent, mais ne peut enregistrer que 10, BMP\f0\par JPG,TGA etc...\par \par \par \par \f2\fs20\par }

______________________________________________________________________

On constate donc, que ce fichier contient un ensemble de balises codées pour changer la façon dont le texte apparaîtra à l'écran. De plus, il peut contenir des images, identifiées par des balises particulières.

ATTENTION: WORD ne respecte pas le standard RichTextFile. Si vous voulez écrire un vrai RichTextFile utilisez WORDPAD.

3-5. Les fichiers avec entête et les fichiers sans entête.

Un simple fichier type texte portant l'extension ".txt" n'a pas d'entête. Le premier octet du fichier (octet zéro) est le premier caractère du texte. Il n'a pas besoin d'entête puisque sa structure est connue et répond à un standard très simple, une suite de caractères terminée par un caractère spécifique représentant la fin de chaque ligne. Mais pour certains fichiers, il est nécessaire de posséder certaines informations pour pouvoir utiliser les données. Un fichier vidéo (.AVI) par exemple, possède un entête de fichier. C'est une certaine quantité d'informations placée en début, et qui donne des renseignements sur le format de la vidéo (hauteur/largeur), la durée de la vidéo etc..

Voici la description de l'entête d'un méta fichier (dessin vectoriel) documentation MicroSoft.

The ENHMETAHEADER structure contains enhanced-metafile data such as the dimensions of the picture stored in the enhanced metafile, the count of records in the enhanced metafile, the resolution of the device on which the picture was created, and so on.

This structure is always the first record in an enhanced metafile.

typedef struct tagENHMETAHEADER { // enmh
   DWORD iType;
   DWORD nSize;
   RECTL rclBounds;
   RECTL rclFrame;
   DWORD dSignature;
   DWORD nVersion;
   DWORD nBytes;
   DWORD nRecords;
   WORD nHandles;
   WORD sReserved;
   DWORD nDescription;
   DWORD offDescription;
   DWORD nPalEntries;
   SIZEL szlDevice;
   SIZEL szlMillimeters;
   DWORD cbPixelFormat;
   DWORD offPixelFormat;
   DWORD bOpenGL;
} ENHMETAHEADER;

3-6. Cas particulier, le fichier avec l'extension ".RAW".

C'est un fichier sans entête. Une suite continue d'octets. Il ne répond à aucun standard. Il peut représenter un texte, une image ou n'importe quoi d'autre. Ce qu'il contient ne peut être lu correctement seulement si on connaît la structure des données enregistrées. Il est aussi définit comme "fichier de données brutes", très employé dans de nombreuses applications scientifiques, en particulier les machines de laboratoire faisant des acquisitions de données.

4. Les routines de gestions de fichiers.

Que nous offre DELPHI pour la gestion des fichiers ?

Tout d'abord, les mêmes fonctions et procédures qui existaient dès la création du TURBO-PASCAL par BORLAND, du moins de par leurs noms et similitudes d'utilisation. Ceci a été un très grand atout pour les développeurs quand ils sont passés à PASCAL-POUR-WINDOWS, puis à DELPHI. Le langage est resté le même, mais il a été considérablement enrichi.


Ensuite, les routines existantes dans le système d'exploitation WINDOWS qui sont accessibles. Les concepteurs de DELPHI l'on prévu.

4-1. Création d'un fichier : Quelle méthode utiliser ?

Pour pouvoir utiliser un fichier, il faut évidemment qu'il soit existant ou bien le créer. La création d'un fichier consiste à entrer dans la table d'allocation de fichier (sur le disque) les informations qui permettrons sont utilisation (nom, attributs, date etc…) et l'adresse de début des données du fichier sur le disque. La seule donnée primordiale est le nom du fichier. Il sera créé avec les attributs par défaut du système et avec la date du jour. NB : Le chemin du fichier accompagne son nom. Le système d'exploitation WINDOWS met à disposition du développeur toutes sortes de fonctions, tant pour la gestion graphique, les communications et la gestion de fichiers. Ce sont les fameuses API. La plupart de ces fonctions sont directement implémentés par DELPHI. On peut donc les utiliser (à priori) sans problème. Cependant certaines de ces fonctions sont d'une utilisation délicate ou particulièrement technique. Sans doute pour ces raisons, les créateurs de DELPHI ont choisit de conserver les routines PASCAL standard.

Pour créer un nouveau fichier il existe la fonction API "FileCreate" voici ce que nous dit l'aide de DELPHI.

function FileCreate(const FileName: string): Integer;

Description

La fonction FileCreate crée un nouveau fichier avec le nom spécifié. Si la valeur renvoyée est positive, la fonction s'est bien déroulée et cette valeur correspond au descripteur du nouveau fichier. Si la valeur renvoyée vaut -1, cela indique qu'une erreur s'est produite.

Remarque : L'utilisation de gestionnaires de variables de fichier Pascal non natif tels que FileCreate est déconseillée. Ces routines correspondent aux fonctions API Windows et renvoient des handles de fichier, pas des variables de fichier Pascal normales. Il s'agit de routines d'accès au fichier de bas niveau. Pour des opérations normales sur les fichiers, utilisez plutôt AssignFile, Rewrite et Reset.


Cela parait un peu confus. Comme si les concepteurs de DELPHI refusaient l'utilisation des API mais en donnant tout de même l'accès au risque du développeur. En fait il n'en est rien. Les fonctions API de gestion des fichiers sont très utilisées, mais pour des constructions particulières de code. L'utilisation des API WINDOWS pour la gestion de fichiers fait appel à des concepts qui dépassent ce tutorial. Je le précise simplement pour mettre en garde le débutant.

Je vais donc me borner dans cette première partie de l'article à n'utiliser que les méthodes PASCAL standard, hormis certaines API dont nous aurons tout de même besoin.
Il est indispensable d'accéder à certaines informations et gestions de fichiers que seul le système d'exploitation est capable de gérer. Les concepteurs de DELPHI ont donc adapté l'implémentation de certaines de ces fonctions des API pour que le développeur puisse les utiliser de façon transparente comme si il s'agissait de pures fonctions PASCAL natives.

Remarque importante : De nombreux objets DELPHI comme le TMemo, ou le TStringList et bien d'autres possèdent des méthodes permettant de charger ou de sauvegarder directement le contenu d'un fichier.

Exemple :

  Memo1.Lines.LoadFromFile('C:\MonTexte.txt');

C'est un automatisme apporté à l'objet qui simplifie le travail du développeur. Mais, le nombre de ces méthodes est limité, et ne nous apprend rien sur le fonctionnement et sur la gestion des fichiers.

4-2. Les principales routines de gestion de fichier en PASCAL.

   
AssignFile : Associer un nom de fichier à une variable fichier.
Rewrite : Pour créer un nouveau fichier.
Reset : Pour ouvrir un fichier existant.
Read : Pour lire le contenu d'un fichier typé.
ReadLn : Pour lire le contenu d'un fichier texte (TextFile).
Write : Pour écrire dans un fichier typé.
Append : Pour écrire dans un fichier texte en mode ajout.
BlockRead : Pour lire dans un fichier non typé.
BlockWrite : Pour écrire dans un fichier non typé.
FileSize : Pour connaitre le nombre d'enregistrements d'un fichier typé, ou la taille en octets
d'un fichier non typé. (ne fonctionne pas avec les fichiers texte)
FileClose : fermer un fichier.
Seek : Placer le pointeur de fichier à une position d'enregistrement.
DeleteFile : Supprimer un fichier existant.
RenameFile : Changer le nom d'un fichier.
FileGetAttr : Renvoit les attributs d'un fichier.



La valeur envoyée par FileGetAttr renvoie les attributs du fichier selon le type prédéfini TSearchRec.

Voici la description du type TSearchRec (documentation de l'aide de DELPHI).

 
Sélectionnez

type 
  TSearchRec = record
                Time : Integer;
                Size : Integer;
                Attr : Integer;
                Name : TFileName;
                ExcludeAttr : Integer;
                FindHandle : THandle;
                FindData : TWin32FindData;
               end;
 
 
Constante Valeur Description
     
faReadOnly $00000001 Fichiers en lecture seule
faHidden $00000002 Fichiers cachés
faSysFile $00000004 Fichiers système
faVolumeID $00000008 Fichiers d'identification de volume
faDirectory $00000010 Fichiers répertoire
faArchive $00000020 Fichiers archive
faAnyFile $0000003F Tout fichier



On se doute bien que la fonction FileGetAttr recherche les information dans la table d'allocation de fichier, et non dans le fichier lui-même. FileGetAttr utilise la fonction GetFileAttributes implémenté dans l'unité WINDOWS, cette une API.

Ce chapitre est sans doute un peu difficile, mais il expose des notions fondamentales sur la gestion des fichiers sous environnement WINDOWS.

5. Fichiers typés et non typés.

Un fichier typé est un fichier construit selon un type défini de données. Le type de donnée peut être un type prédéfini par le langage ou bien un type de donnée définit, pour un besoin particulier, par le développeur.

Un fichier non typé est un fichier qui n'a pas de type particulier. On pourra donc y enregistrer n'importe quoi à condition de gérer correctement les données.

Le mot réservé File permet de définir un identificateur de fichier typé ou non typé.

5-1. Déclaration d'un identifiant de fichier

Déclaration d'un fichier non typé.

 
Sélectionnez

 Var
    Fichier : File;

Déclaration d'un identifiant de fichier selon un type prédéfinit.

 
Sélectionnez

Var
  Fichier : File Of Integer;

Un type fichier prédéfinit, le type fichier texte.

 
Sélectionnez

Var
  Fichier : TextFile;



Eclaircissement et précision importants pour le débutant :

J'ai expliqué en chapitre -3-, que les fichiers tableurs CSV, le fichier INI et le fichier HTM ont exactement le même format qu'un fichier texte portant l'extension TXT.
Cependant il existe le type prédéfinit TIniFile. C'est une classe qui permet grâce à des automatismes de gérer assez facilement les rubriques du fichier et ce qu'elles contiennent. Il y a un excellent tutoriel dans la FAQ DELPHI du site à cette adresse : FICHIERS INI

Si vous voulez créer et utiliser un fichier CSV ou HTM, vous le déclarerez et vous l'utiliserez exactement comme un fichier texte.
NB : Vous pouvez en faire de même pour un fichier INI. En ce cas, vous devrez écrire vous-même les routines pour en extraire ou y rentrer des données, ce que fait la classe TIniFile.

5-2. Les structures dans les fichiers

Déclaration d'un identifiant de fichier selon un type définit pour un besoin.

 
Sélectionnez

Type
   TDonnees = Record
                 ValeurEntiere  : Integer;
                 ValeurDecimale : Double;
              End;

Var
   Fichier : File Of TDonnees;

Continuons avec un fichier typé simple. Reprenons la structure définie plus haut :

Le type de donnée "TDonnees" étant de dimension connue son écriture et sa lecture seront réduites au plus simple.

 
Sélectionnez

Var
  Donnees : TDonnees;
  Fichier : File Of TDonnees;

Begin
   Donnees.ValeurEntiere := 1298;
   Donnees.ValeurDecimale := 78.123;
   AssignFile(Fichier,'Nom_du_fichier');
   Rewrite(Fichier);
   Write(Fichier,Donnees);
   CloseFile(Fichier);
End;

Nous avons en une seule opération (Write), enregistré toutes les données. Quelles que soient les valeurs contenues dans les composantes de la structure, sa dimension restera la même 4 octets pour la variable "ValeurEntiere" et 8 octets pour la variable "ValeurDecimale".

La lecture sera aussi simple.

 
Sélectionnez

Type
    TDonnees = Record
                  ValeurEntiere  : Integer;
                  ValeurDecimale : Double;
               End;

Var
  Donnees : TDonnees;
  Fichier : File Of TDonnees;

Begin
   Donnees.ValeurEntiere := 1298;
   Donnees.ValeurDecimale := 78.123;
   AssignFile(Fichier,'Nom_du_fichier');
   Reset(Fichier);
   Read(Fichier,Donnees);
   CloseFile(Fichier);
End;

5-3. Organisation d'un fichier

Fichier à organisation séquentielle.
Fichier à organisation relative.
Fichier à organisation indexée.


Les enregistrements d'un fichier séquentiel sont écrits sur le support physique dans l'ordre physique dans lequel ils sont entrés. Il y a donc une correspondance entre l'ordre physique et l'ordre logique.

Dans un fichier à organisation relative, les enregistrements sont de taille fixe. Ces enregistrements portent un numéro relatif au début du fichier, de zéro à N. Chaque numéro représente l'emplacement sur le disque.

Dans l'organisation indexée, chaque enregistrement est identifié par un numéro (une clef) et à chaque clef est associé un numéro d'enregistrement qui renvoie à l'enregistrement correspondant.

NB : Un fichier séquentiel peut être composé d'enregistrements de taille fixe. On peut ajouter des blocs de données au fichier, en retirer, et modifier des blocs sans avoir besoin de charger tout le fichier. C'est cette méthode que nous allons utiliser dans notre didacticiel.

Image non disponible



Exemple de définition d'un fichier a structure fixe.

 
Sélectionnez

Type
    // Définition de la structure d'enregistrement
   TDonnees = Record
                 Nom       : String[40];
                 PreNom    : String[40];
                 Adresse   : String[80];
                 Telephone : String[20];
                 Age       : Integer;
              End;

Var
   Donnees    : TDonnees;
   Fichier    : File Of TDonnees;
   NomFichier : String;
   Indice     : Integer;

Nous avons une vaviable "Donnees" dont l'identificateur est définit, donc sa taille est connue : 41 + 41 + 81 + 21 + 4 = 188 octets. La fonction FileSize renvoi le nombre d'enregistrements selon le type de fichier définit et non pas le nombre d'octets qu'il occupe sur le disque. Le fichier étant du type TDonnees qui fait 188 octet, si le fichier occupe 188 octets il y a 1 seul enregistrement : FileSize renverra 1.

6. Les types String et AnsiString dans les structures.

Le type AnsiString est le type de chaîne de caractère par défaut. Elle n'a pas de mémoire allouée à sa déclaration, c'est un pointeur.

"Le mot réservé string fonctionne comme un identificateur de type générique" nous dit l'aide de DELPHI.

ShortString est aussi défini comme "ancien type string". Qu'est ce donc que l'ancien type string ? A l'origine du Turbo-Pascal, les machines travaillaient en 8 puis 16 Bits. Le type string était un tableau prédéfinit de 255 caractères. Le caractère zéro (un octet) était utilisé pour indiquer la longueur de la chaîne (0 à 255). Cela a été un très grand atout sur le C, car le type string a considérablement simplifié la programmation avec des chaînes de caractères. Le type ShortString peut être redéfinit dans sa longueur. Exemple :

 
Sélectionnez

Var
  Chaine1 : ShortString;// Chaîne de 255 caractères
  Chaine2 : ShortString[100] ;// Chaîne de 100 caractères

Le type AnsiString est un pointeur de caractères. Donc, une variable qui n'a pas de mémoire allouée par défaut contrairement au type ShortString.
Tout ceci nous donne :

 
Sélectionnez

Var
   Chaine1 : Array[0..255] Of Char;// ShortString
   Chaine2 : ^Char;     // AnsiString

Donc, si nous écrivons le code suivant :

 
Sélectionnez

Var
   Chaine1 : Array[0..255] Of Char;
   Chaine2 : ^Char;     
   C       : Char;

begin
   C := Chaine1[8]; // récupère le huitième caractère de la chaîne
   C := Chaine2[8]; // Provoque une erreur d'exécution avec violation d'adresse.

Nous devons donc allouer de la mémoire à la variable Chaine2 pour pouvoir l'utiliser.

 
Sélectionnez

Var
   Chaine1 : Array[0..255] Of Char;
   Chaine2 : ^Char;     
   C       : Char;

begin
   C := Chaine1[8]; // récupère le huitième caractère de la chaîne
   GetMem(Chaine2,10); // Allouer 10 octets à la variable  
   C := Chaine2[8];

Et le type String dans tout ça ? Comme il est dit plus haut, le mot réservé String fonctionne comme identificateur de type générique. Le compilateur Pascal va compléter et/ou modifier le code au moment de la construction du programme.
Donc, reprenons :

Allouons de la mémoire à la variable Chaine2.

 
Sélectionnez

Var
   Chaine1 : String[80];// ShortString de 80 caractères (255 maxi)
   Chaine2 : String;    // AnsiString

begin
   C := Chaine1[8]; // récupère le huitième caractère de la chaîne
   Chaine2 := 'Je vous salut tous';
   C := Chaine2[4]; // C = 'v'

Mais, le compilateur aura traduit le code de la façon suivante :

 
Sélectionnez

begin
   C := Chaine1[8]; // récupère le huitième caractère de la chaîne
   GetMem(Chaine2,SizeOf('Je vous salut tous'));
   Chaine2 := 'Je vous salut tous';
   C := Chaine2[4]; // C = 'v'

Ce mécanisme fait partie des automatismes du langage Pascal de BORLAND qui a considérablement simplifié l'usage des pointeurs.

6-1. Pourquoi avoir conservé l'ancien type String et créé des automatismes sur l'usage des pointeurs ?

pour assurer une compatibilité ascendante.
pour simplifier le travail du développeur.

Mais il y a d'autres raisons, complémentaires.

Dans le cas de travail avec des chaînes courtes, la vitesse d'exécution est très grande car le passage de valeurs à un tableau de longueur fixe est direct. L'utilisation d'un pointeur nécessite au préalable une allocation mémoire (GetMem) après avoir calculé la quantité de mémoire à réserver (SizeOf).
Il n'est pas possible de travailler avec une structure (Record) de chaînes AnsiString si elle doit être enregistrée dans un fichier. Pour être enregistrée dans un fichier, une structure doit avoir une dimension fixe et connue d'avance. Donc le type ShortString est lui seul compatible dans une structure pour un fichier.

Dans le code suivant :

 
Sélectionnez

Type
   TChaine = Record
                I : Integer;
                C : String;
             End;

var
  C    : TChaine;
  F    : File Of TChaine;

Le compilateur s'arrête sur F : File Of TChaine; indiquand [Erreur] Unit1.pas(24): Le type 'TChaine' nécessite une finalisation - non autorisé dans type fichier

 
Sélectionnez

Type
   TChaine = Record
                I : Integer;
                C : String[60];
             End;

var
  C    : TChaine;
  F    : File Of TChaine;

Même chose pour les structures de tableaux dynamiques qui sont des pointeurs.

 
Sélectionnez

type
   TF = Record
           Tab : Array Of Array Of Integer;
        End;

Var
   F  : File Of TF;

Provoque une erreur de compilation.

7. Continuons avec une fonction simple, copie de fichier de n'importe quel type.

Voici une routine de copie de fichier de n'importe quel type (texte ou binaire), et qu'il soit Caché, system, ou en lecture seule.

On déclare le fichier à copier comme étant non typé et on fait appel au procédures BlockRead et BlockWrite.

 
Sélectionnez

//---------------------------------------------------------------
Function Copie(Source,Destination : String;Attribus : Integer) : Integer;

Var
   Fd,FS    : File;
   NL,NE    : Integer;
   Buf      : Array[1..100000] Of Byte;

Begin
   Result := -1;
   // Vider éventuellement le tampon d'erreur d'entrée/Sortie
   IoResult;
   // Fixer le mode lecture en lecture seule pour éviter les
   // erreurs de lecture si le fichier est en lecture seule.
   FileMode := 0;
   // Associer le nom du fichier à la variable FD.
   Assignfile(FD,Destination);
   // Créer un fichier en mode écriture avec une taille de base
   // de 1 octet
   Rewrite(FD,1);
   // Associer le nom du fichier à la variable FS.
   AssignFile(FS,Source);
   // Ouvrir un fichier en mode lecture avec une taille de base
   // de 1 octet
   Reset(FS,1);
   Repeat
       Application.ProcessMessages;
       // Lire un paquet de données dans le fichier source
       BlockRead(FS,Buf,SizeOf(Buf),NL);
       // Ecrire dans le destinataire autant de données lues.
       BlockWrite(FD,Buf,NL,NE);
       // Si une erreur est detecté, quitter la fonction.
       If IoResult <> 0 Then Exit;
   Until (NL = 0) Or (NE <> NL);

   CloseFile(FD);
   CloseFile(FS);
   // Fixer les attributs du fichier
   Result := FileSetAttr(Destination,Attribus);
End;
//---------------------------------------------------------------

C'est ce que l'on appelle la copie en mode RAW ou bit-à-bit.
Cette méthode de copie convient pour tout type de support, disque dur, mémoire USB ou dossier réseaux, excepté les supports en lecture seule comme les CDROMs bien sur.

8. Gestion d'un fichier texte.

Un fichier texte est un ensemble d'enregistrements dont chaque groupe de caractères représentant une ligne, est terminé par un caractère 13. C'est la procédure WriteLn qui se charge d'ajouter le code 13 (retour chariot) à la fin de chaque ligne. A la lecture, la procédure ReadLn lira autant de données (d'octets) jusque à trouver un code 13. Le pointeur de fichier sera placé immédiatement après le code 13, et le prochain appel à la procédure ReadLn, la lecture se poursuivra à partir de ce caractère. La fin d'un fichier texte est quelques fois suivie d'un code "Control Z" ou 26 en décimale.

Image non disponible

Lecture et écriture d'un fichier texte.

 
Sélectionnez

Procedure Lecture(Nom : String);
Var
   F   : TextFile;
   Chn : String;

Begin
   // Assigner le nom du fichier Nom à la variable F
   AssignFile(F,Nom);
   // Ouvrir le fichier en position zéro.
   Reset(F);
   // Lire le fichier tant que la fin n'est pas atteinte.
   While Not Eof(F) Do
    begin
       ReadLn(F,Chn);
       // Traitement quelconque de la chaîne de caractères Chn.
    
    end;
   // Fermeture du fichier.
   CloseFile(F);
End;

//========================================================

Var
   Tableau  : Array[1..10] Of String;

Procedure Ecriture(Nom : String);
Var
   F   : TextFile;
   Chn : String;

Begin
   // Assigner le nom du fichier Nom à la variable F
   AssignFile(F,Nom);
   // Ouvrir le fichier en position zero.
   Rewrite(F);
   // Lire le fichier tant que la fin n'est pas atteinte.
   For L := 1 To 10 Do
    Begin
       Chn := Tableau[L];
       WriteLn(F,Chn);
    end;
   // Fermeture du fichier.
   CloseFile(F);
End;

Les procédures et fonctions Seek et FileSize ne sont pas utilisables pour un fichier texte, ou plus exactement déclaré comme TextFile.
Mais nous verrons en annexe quelques trucs et astuces qui peuvent s'avérer bien pratiques.

9. Gestion d'un fichier binaire

Un fichier binaire peut être construit selon un format devant stocker n'importe quel type (ou genre) de données. Il peut contenir une image, une base de données, un tableur. Et aussi, une vidéo ou de la musique, un répertoire téléphonique ou un agenda. Il sera construit selon une structure (ou un format) précise et devant répondre à un besoin spécifique.
On comprend donc, que seule l'imagination est la limite d'une nouvelle structure, d'un nouveau fichier.

10. Mise en pratique d'un fichier binaire, création d'un agenda téléphonique.

Pour poursuivre ce didacticiel, nous allons pas à pas construire un agenda téléphonique, avec photo d'identité et un petit texte de commentaires. Nous verrons comment trier les données selon un critère particulier et comment naviguer de façon simple et rapide dans ce type de fichier.

Base de la gestion du fichier.

 
Sélectionnez

Type
    // Définition de la structure d'enregistrement
   TDonnees = Record
                 Nom       : String[40];
                 PreNom    : String[40];
                 Adresse   : String[80];
                 Telephone : String[20];
                 Age       : Integer;
              End;

Var
   Donnees    : TDonnees;
   Fichier    : File Of TDonnees;
   NomFichier : String;
   Indice     : Integer;

//-----------------------------------------------------------------------
Procedure Enregistre(Donnees : TDonnees; Indice : Integer);
Begin
   // Placer le pointeur de fichier à la position "Indice"
   Seek(Fichier,Indice);
   // Ecrire les 188 octets de la variable "Donnees"
   Write(Fichier,Donnees);
End;
//-----------------------------------------------------------------------
Procedure Lecture(Var Donnees : TDonnees; Indice : Integer);
Begin
   // Placer le pointeur de fichier à la position "Indice"
   Seek(Fichier,Indice);
   // Lire 188 octets coresspondants à la variable "Donnees"
   Read(Fichier,Donnees);
End;
//-----------------------------------------------------------------------
Procedure OuvreFichier(Nom : String);
Begin
   AssignFile(Fichier,Nom);
   If Not FileExists(Nom) Then Rewrite(Fichier)
   Else Reset(Fichier);
End;
//-----------------------------------------------------------------------
Procedure FermeFichier;
Begin
   CloseFile(Fichier);
End;

Nous n'avons pas besoin de plus pour gérer notre agenda téléphonique. Bien sur, nous avons besoin d'une interface utilisateur (aussi appelé Interface Homme/Machine) pour saisir et afficher les données.

10-1. Mettons en pratique le mécanisme, avec une saisie, une navigation et un enregistrement des données.

Exercice -1-

Image non disponible



 
Sélectionnez

unit Exercice1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, Buttons, Menus, Spin;

type
  TFichePrincipale = class(TForm)
    EditNom: TEdit;
    EditPrenom: TEdit;
    EditAdresse: TEdit;
    EditTelephone: TEdit;
    EditAge: TEdit;
    Label1: TLabel;
    Label2: TLabel;
    Label3: TLabel;
    Label4: TLabel;
    Label5: TLabel;
    MainMenu1: TMainMenu;
    Fichier1: TMenuItem;
    Nouveau1: TMenuItem;
    Ouvrir1: TMenuItem;
    Quitter1: TMenuItem;
    BtPrecedent: TBitBtn;
    BtSuivant: TBitBtn;
    StaticTextIndice: TStaticText;
    Label6: TLabel;
    SpinEdit1: TSpinEdit;
    Label7: TLabel;
    BtValider: TBitBtn;
    OpenDialog1: TOpenDialog;
    SaveDialog1: TSaveDialog;
    StaticTextNbEnr: TStaticText;
    Label8: TLabel;
    BtRecherche: TButton;
    BtAjoute: TBitBtn;
    procedure Nouveau1Click(Sender: TObject);
    procedure Ouvrir1Click(Sender: TObject);
    procedure SpinEdit1KeyPress(Sender: TObject; var Key: Char);
    procedure BtRechercheClick(Sender: TObject);
    procedure BtValiderClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure BtAjouteClick(Sender: TObject);
    procedure BtPrecedentClick(Sender: TObject);
    procedure BtSuivantClick(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    { Déclarations privées }
  public
    { Déclarations publiques }
    Procedure ActualiseAffichage;
  end;

var
  FichePrincipale: TFichePrincipale;

implementation

{$R *.DFM}


Type
   TDonnees = Record
                 Nom       : String[40];
                 PreNom    : String[40];
                 Adresse   : String[80];
                 Telephone : String[20];
                 Age       : Integer;
              End;

Var
   Donnees        : TDonnees;
   Fichier        : File Of TDonnees;
   NomFichier     : String;
   TailleFichier,
   NbDonnees,
   Indice         : Integer;




{-------------------------------------------------------------------}
{       Indique si une chaîne de caractères représente une valeur   }
{       entière valide.                                             }
{-------------------------------------------------------------------}
Function ChaineEntierValide(Chn : String) : Boolean;
Var
  I  : Integer;
Begin
   Result:=False;
   If (Length(Chn) = 0) Or (Chn = '-') Then Exit;
   For I:=1 To Length(Chn) Do
    If Not (Chn[I] In['0'..'9']) Then Exit;
   Result:=True;
End;
//-----------------------------------------------------------------------
Procedure Enregistre(Donnees : TDonnees; Indice : Integer);
Begin
   Seek(Fichier,Indice);
   Write(Fichier,Donnees);
   // Forcer l'écriture physique du fichier (Tampon disque -> Disque)
   CloseFile(Fichier);
   Reset(Fichier);
End;
//-----------------------------------------------------------------------
Procedure Lecture(Var Donnees : TDonnees; Indice : Integer);
Begin
   // Placer le pointeur de fichier à la position correspondante.
   Seek(Fichier,Indice);
   // Lire in bloc de données
   Read(Fichier,Donnees);
End;
//-----------------------------------------------------------------------
Procedure OuvreFichier(Nom : String);
Begin
   AssignFile(Fichier,Nom);
// Si le fichier n'existe pas, le créer si non l'ouvrir
   If Not FileExists(Nom) Then Rewrite(Fichier)
   Else Reset(Fichier);
End;
//-----------------------------------------------------------------------
Procedure FermeFichier;
Begin
   CloseFile(Fichier);
End;
//-----------------------------------------------------------------------

procedure TFichePrincipale.FormCreate(Sender: TObject);
begin
   Indice :=0;
end;
//-----------------------------------------------------------------------
procedure TFichePrincipale.FormClose(Sender: TObject; var Action: TCloseAction);
begin
   FermeFichier;
end;

Nous venons d'écrire les principales routines de gestion du fichier.
Continuons avec la gestion de l'interface utilisateur.

Nous devons actualiser les affichages des données en cours à chaque modification.

 
Sélectionnez

//-----------------------------------------------------------------------
procedure TFichePrincipale.ActualiseAffichage;
begin
   With Donnees Do
    begin
       EditNom.Text := Nom;
       EditPrenom.Text := Prenom;
       EditAdresse.Text := Adresse;
       EditTelephone.Text := Telephone;
       EditAge.Text := IntToStr(Age);
    end;
   StaticTextNbEnr.Caption := IntToStr(NbDonnees);
   SpinEdit1.MaxValue := NbDonnees;
   StaticTextIndice.Caption := IntToStr(Indice + 1);
end;
//-----------------------------------------------------------------------
// Création d'un nouveau fichier.
//-----------------------------------------------------------------------
procedure TFichePrincipale.Nouveau1Click(Sender: TObject);
begin
   If SaveDialog1.Execute Then
    begin
       NomFichier := SaveDialog1.FileName;
       If FileExists(NomFichier) Then
        begin
           If MessageDlg('Le fichier <' + NomFichier + '> existe !' + 
                          #13#10 +
                         'Faut il le remplacer ',
                         mtConfirmation,[mbYes,mbNo],0) = mrNo Then Exit
           Else DeleteFile(NomFichier);
        end;
      OuvreFichier(NomFichier);
      NbDonnees := 0;
    end;
end;
//-----------------------------------------------------------------------
// Ouverture d'un fichier existant.
//-----------------------------------------------------------------------
procedure TFichePrincipale.Ouvrir1Click(Sender: TObject);
begin
   If OpenDialog1.Execute Then
    begin
       NomFichier := OpenDialog1.FileName;
       If Not FileExists(NomFichier) Then
        begin
           If MessageDlg('Le fichier <' + NomFichier + '> n''existe pas !'
                          + #13#10 +
                         'Faut il le créer ?',
                         mtConfirmation,[mbYes,mbNo],0) = mrNo Then Exit;
        end;
       OuvreFichier(NomFichier);

       // FileSize renvoi le nombre d'enregistrements selon son type
       // définit et non pas le nombre d'octets qu'il occupe sur le disque.
       // Le fichier étant du type TDonnees qui fait 184 octet, si le 
       // fichier occupe 184 octets il y a 1 seul enregistrement.
       TailleFichier := FileSize(Fichier);
       If  TailleFichier >= 1 Then
        begin
           NbDonnees := TailleFichier;
           Lecture(Donnees,0);
           SpinEdit1.Value := NbDonnees;
        end;
       ActualiseAffichage;
    end;
end;
//-----------------------------------------------------------------------
// Définition de l'indice de recherché.
//-----------------------------------------------------------------------
procedure TFichePrincipale.SpinEdit1KeyPress(Sender: TObject; var Key: Char);
begin
   // Limiter la saisie aux chiffres. #8 est le code de la touche 
   // d'effacement
   If Not (Key In['0'..'9',#8]) Then Key := #0;
end;
//-----------------------------------------------------------------------
// Rechercher les données d'un élément selon son indice dans le fichier.
//-----------------------------------------------------------------------
procedure TFichePrincipale.BtRechercheClick(Sender: TObject);
Var
   Chn    : String;

begin
   Chn := SpinEdit1.Text;
   If ChaineEntierValide(Chn) Then
    begin
       Indice := SpinEdit1.Value - 1;
       If (Indice <= NbDonnees)  Then
        begin
           Lecture(Donnees,Indice);
           ActualiseAffichage;
        end;
    end;
end;
//-----------------------------------------------------------------------
// Valider les modifications d'un élément de l'agenda.
//-----------------------------------------------------------------------
procedure TFichePrincipale.BtValiderClick(Sender: TObject);
begin
   With Donnees Do
    begin
       Adresse := EditAdresse.Text;
       Nom := EditNom.Text;
       PreNom := EditPrenom.Text;
       Telephone := EditTelephone.Text;
       Age := StrToInt(EditAge.Text);
    end;
   Enregistre(Donnees,Indice);
end;



Nous devons pouvoir ajouter un élément à l'agenda. Cet élément sera écrit en fin de fichier.

 
Sélectionnez

//-----------------------------------------------------------------------
// Ajouter un élément à l'agenda.
//-----------------------------------------------------------------------
procedure TFichePrincipale.BtAjouteClick(Sender: TObject);
begin
   With Donnees Do
    begin
       Adresse := EditAdresse.Text;
       Nom := EditNom.Text;
       PreNom := EditPrenom.Text;
       Telephone := EditTelephone.Text;
       Age := StrToInt(EditAge.Text);
    end;
   Enregistre(Donnees,TailleFichier);
   TailleFichier := FileSize(Fichier);
   NbDonnees := TailleFichier;
   Indice:=NbDonnees - 1;
   SpinEdit1.Value := NbDonnees;
   ActualiseAffichage;
end;



Ajoutons deux boutons pour naviguer dans l'agenda.

 
Sélectionnez

//-----------------------------------------------------------------------
// Lire l'enregistrement précédent dans le fichier.
//-----------------------------------------------------------------------
procedure TFichePrincipale.BtPrecedentClick(Sender: TObject);
begin
   If  NbDonnees >= 1 Then
    begin
       If Indice > 0 Then
        begin
           Dec(Indice);
           Lecture(Donnees,Indice);
        end;
    end;
   ActualiseAffichage;
end;
//-----------------------------------------------------------------------
// Lire l'enregistrement suivant dans le fichier.
//-----------------------------------------------------------------------
procedure TFichePrincipale.BtSuivantClick(Sender: TObject);
begin
   If  NbDonnees >= 1 Then
    begin
       If Indice < NbDonnees - 1 Then
        begin
           Inc(Indice);
           Lecture(Donnees,Indice);
        end;
    end;
   ActualiseAffichage;
end;
//-----------------------------------------------------------------------

end.

10-2. Comment supprimer un enregistrement ?

La solution est de passer par un fichier temporaire dans lequel nous copierons tous les éléments excepté celui que nous voulons supprimer.

Exercice 2.

Image non disponible



Ajoutons une variable pour le fichier temporaire.

 
Sélectionnez

implementation

{$R *.DFM}


Type
   TDonnees = Record
                 Nom       : String[40];
                 PreNom    : String[40];
                 Adresse   : String[80];
                 Telephone : String[20];
                 Age       : Integer;
              End;

Var
   Donnees        : TDonnees;
   FichierBak,     // Identificateur du fichier temporaire
   Fichier        : File Of TDonnees;
   NomFichier     : String;
   TailleFichier,
   NbDonnees,
   Indice         : Integer;



Ajoutons la procédure de suppression.

 
Sélectionnez

//-----------------------------------------------------------------------
procedure TFichePrincipale.BtSupprimeClick(Sender: TObject);
Var
   I  : Integer;

begin
   // Création d'un fichier temporaire
   AssignFile(FichierBak,'FichierTemp');
   If FileExists('FichierTemp') Then DeleteFile('FichierTemp');
   Rewrite(FichierBak);

   // Copier tous les enregistrements du fichier d'origine à
   // l'exception de l'enregistrement à supprimer
   For I := 0 To NbDonnees - 1 Do
    begin
       If I <> Indice Then
        begin
           Seek(Fichier,I);
           Read(Fichier,Donnees);
           Write(FichierBak,Donnees);
        end;
    end;
   Dec(NbDonnees);

   // Fermer les fichier
   CloseFile(FichierBak);
   CloseFile(Fichier);

   // Supprimer le fichier d'origine
   DeleteFile(NomFichier);

   // Renomer le fichier temporaire avec le nom d'origine
   RenameFile('FichierTemp',NomFichier);

   // Actualliser
   OuvreFichier(NomFichier);
   If NbDonnees > 0 Then
    If Indice > 0 Then
     begin
        Dec(Indice);
        Lecture(Donnees,Indice);
     end;
   ActualiseAffichage;
end;

end.

Ce petit programme peut bien sur être grandement amélioré et sécurisé. Nous allons aborder maintenant la notion de flux.

11. Qu'est ce qu'un flux ?

Un flux est un paquet de données transféré entre un fichier et un programme, et inversement. Le fichier sera dirigé vers un disque, mais il peut aussi être dirigé vers un autre support comme la mémoire vive. On parle alors généralement de fichier virtuel. On parle aussi d'enregistrement au fil de l'eau. C'est-à-dire que les données d'un calcul par exemple, sont enregistrées au fur et à mesure de la sortie des résultats.

En anglais, le mot "stream" signifie cours d'au ou ruisseau. Le mot "Stream" a donc été choisi pour désigner un flux.

Nous disposons avec DELPHI, d'une classe de base représentant un flux, la classe "TStream". Mais cette classe ne doit pas être instanciée (Voir l'aide de DELPHI). Nous disposons de plusieurs descendants de la classe TStream.

TFileStream (pour l'utilisation de fichiers).
TStringStream (pour la manipulation de chaînes en mémoire).
TMemoryStream (pour l'utilisation d'un tampon mémoire).
TBlobStream (pour l'utilisation de champs BLOB).
TWinSocketStream (pour la lecture et l'écriture sur une connexion socket).
TOleStream (pour l'utilisation d'une interface COM en lecture et écriture).

12. Reprenons notre agenda téléphonique et ajoutons une photo en face de chaque membre.

Nous-nous trouvons là, devant le problème évoqué aux chapitres -5- et -6-. La variable devant contenir l'image représentant la photo, doit avoir une dimension connue et fixée à l'avance si elle fait partie d'une structure devant être enregistrée dans un fichier. La seule solution est de définir un tableau de dimension fixe aussi grand que le fichier contenant l'image d'origine.

Nous pourrons alors, transférer directement les données de l'image BITMAP dans ce tableau d'octets.

Cette solution nous limite à des formats, types et images de structure identiques, ou, ne devant pas dépasser une dimension prévue à l'avance. Vous trouverez en chapitre -22- "Le fichier BitMap démystifié". J'y expose la structure d'un fichier BITMAP.

Les photos ne devront pas dépasser un format fixé à l'avance. Fixons nous à 150 x 150 pixels.

La seule façon de stoker l'image dans la structure de fichier, est d'utiliser un tableau définit avec une dimension suffisante. Si l'image est en 16 millions de pixels, elle occupera (en format 32 bit) 90000 octets plus l'entête définissant l'image soit un total de 90054 octets. Nous allons devoir passer par un flux pour charger et stocker l'image dans la structure.

Exercice 3.

Image non disponible



Modification de la structure.

 
Sélectionnez

Type
   TDonnees = Record
                 Nom         : String[40];
                 PreNom      : String[40];
                 Adresse     : String[80];
                 Telephone   : String[20];
                 Age         : Integer;
                 Photo       : Array[0..90054] Of Byte;
                 TaillePhoto : Integer;
              End;



Procédure pour transférer une image BitMap dans le tableau, et inversement pour restituer l'image dans le BitMap depuis le tableau.

 
Sélectionnez

//-----------------------------------------------------------------------
procedure TFichePrincipale.TransfertPhotoDansStructure;
Var
   Flux : TMemoryStream;
   C, T : Integer;
   Buff : ^Byte;

begin
   Flux := TMemoryStream.Create;
   // Transfert de la photo dans le flux
   Image1.Picture.Bitmap.SaveToStream(Flux);
   // Connaitre la taille en octets du flux
   T := Flux.Size;
      // Attribuer un espace mémoire au buffer
   GetMem(Buff,T);
   // Placer le lecteur du flux en position zéro.
   Flux.Position :=0;
   // Transfert des données de la photo dans le tableau
   Flux.Read(Buff^,T);
   Move(Buff^,Donnees.Photo,T);
   // Memorisation de la taille du flux
   Donnees.TaillePhoto := T;
   Flux.Free;
end;
//-----------------------------------------------------------------------
procedure TFichePrincipale.TransfertPhotoDepuisStructure;
Var
   Flux : TMemoryStream;
   T    : Integer;
   Buff : ^Byte;

begin
   Flux := TMemoryStream.Create;
   // A ce stade, le flux n'a pas d'espace réservé. Il faut donc lui
   // atribuer un espace mémoire correspondant à la taille du bitmap.
   T:= Donnees.TaillePhoto;
   Flux.SetSize(T);
   // Transfert des données de la photo depuis le tableau, dans le flux
   GetMem(Buff,T);
   Move(Donnees.Photo,Buff^,T);
   // Replacer le pointeur de flux à la position 0
   Flux.Seek(0,soFromBeginning);
   Flux.Position :=0;
   Flux.Write(Buff^,T);
   // Replacer le pointeur de flux à la position 0
   Flux.Seek(0,soFromBeginning);
   // Transfert du flux dans le bitmap
   Image1.Picture.BitMap.LoadFromStream(Flux);
   Image1.Refresh;
   Flux.Free;
end;
//-----------------------------------------------------------------------
// Ouverture du fichier BitMap pour la photo d'identité
procedure TFichePrincipale.BtPhotoClick(Sender: TObject);
Var
   Nom  : String;

begin
   If OpenPictureDialog1.Execute Then
    begin
       Nom := OpenPictureDialog1.FileName;
       // Attention, il n'y a pas de sécurité pour la dimension de l'image
       Image1.Picture.LoadFromFile(Nom);
    end;
end;
//-----------------------------------------------------------------------

Nous verrons aux chapitres -15- et -16- comment sécuriser le chargement d'une image (selon ses dimensions), et comment redimensionner un bitmap au bon format.

13. Continuons notre didacticiel avec l'ajout d'un commentaire pour chaque élément.

Le texte sera limité à 255 caractères pour rester dans même logique d'utilisation de la structure.

Exercice 4.

Image non disponible



Complément de la structure avec une chaine de caractères qui contiendra le commentaire.

 
Sélectionnez

Type
   TDonnees = Record
                 Nom         : String[40];
                 PreNom      : String[40];
                 Adresse     : String[80];
                 Telephone   : String[20];
                 Age         : Integer;
                 Photo       : Array[0..90053] Of Byte;
                 TaillePhoto : Integer;
                 Commentaire : ShortString; // 255 caractères maxi
              End;

Complétons les procédures pour l'utilisation du commentaire.

 
Sélectionnez

//-----------------------------------------------------------------------
procedure TFichePrincipale.ActualiseAffichage;
Var
  Chn : String;

begin
   With Donnees Do
    begin
       EditNom.Text := Nom;
       EditPrenom.Text := Prenom;
       EditAdresse.Text := Adresse;
       EditTelephone.Text := Telephone;
       EditAge.Text := IntToStr(Age);
       TransfertPhotoDepuisStructure;
       Chn := Commentaire;
    end;
   // Vérification si la chaîne commentaire contient un texte et
   // ajout dans le mémo.
   If Chn <> '' Then Memo1.Lines.SetText(PChar(Chn));

   StaticTextNbEnr.Caption := IntToStr(NbDonnees);
   SpinEdit1.MaxValue := NbDonnees;
   StaticTextIndice.Caption := IntToStr(Indice + 1);
end;
//-----------------------------------------------------------------------
procedure TFichePrincipale.BtValiderClick(Sender: TObject);
Var
   Chn  : String;
   L, I : Integer;
begin
   L := Memo1.Lines.Count;
   Chn := '';
// Si le Memo contient un texte, ajouter ce texte dans l'enregistrement.
   If L > 0 Then
    Begin
       // Copier le contenu du memo dans la chaine de caractères.
       For I:=0 To L-1 Do Chn := Chn + Memo1.Lines[I];
    end;


   With Donnees Do
    begin
       Adresse := EditAdresse.Text;
       Nom := EditNom.Text;
       PreNom := EditPrenom.Text;
       Telephone := EditTelephone.Text;
       Age := StrToInt(EditAge.Text);
       TransfertPhotoDansStructure;
       Commentaire := Chn;
    end;
   // Enregistrer les données dans le fichier.
   Enregistre(Donnees,Indice);
end;
//-----------------------------------------------------------------id="XV"------

14. Tri des données.

Il nous reste maintenant à trier notre agenda par noms.

Là encore, nous allons devoir réécrire le fichier dans un ordre croissant selon les noms des éléments. C'est grâce à la puissance du compilateur PASCAL de BORLAND que nous pouvons écrire un algorithme simple de tri.

Si nous faisons une comparaison de grandeur entre deux chaînes de caractères, ce n'est pas la longueur de la chaîne que nous obtenons, mais la valeur numérique de la somme des caractères.

La chaîne "ABC" est plus petite que la chaîne "DEF".
Le test si "ABC" est plus petit que "DEF" est traduit par :
 Si (65 + 66 +67) est plus petit que (68 + 69 + 70) .
Une chaîne de caractères est une suite d'octets dont chaque valeur peut aller de 0 à 255.

C'est donc grâce à un test numérique que nous pouvons trier un ensemble de chaînes de caractères.

Principe :

 Chn1 := TableNoms[L].Nom;
 Chn2 := TableNoms[L + 1].Nom;
 If Chn1 > Chn2 Then Valide := True;

Nous allons tout d'abord charger l'ensemble des variables "Nom" de tous les enregistrements, ainsi que la position de l'enregistrement correspondant dans un tableau.

Créons une nouvelle structure pour mémoriser les noms et leurs indices dans le fichier, et un tableau dynamique dont la taille sera défini selon le nombre d'enregistrement de l'agenda.

Exercice 5.

 
Sélectionnez

Type

TTri = Record
              Index   : Integer;
              Nom     : String[40];
           End;


Var
   TableNoms      : Array Of TTri;

Procédure de chargement des variables "Nom" ainsi que leur position correspondante dans le fichier.

 
Sélectionnez

//-----------------------------------------------------------------------
Procedure ChargeNomsAgenda;
Var
  I  : Integer;

Begin
   // Définition de la taille du tableau selon le nombre d'enregistrements
   SetLength(TableNoms,NbDonnees);
   // Plaçon le pointeur de fichier en début.
   Seek(Fichier,0);
   // Lecture de tous les enregistrements.
   For I := 0 To NbDonnees - 1 Do
    begin
       Read(Fichier,Donnees);
       TableNoms[I].Index := I;
       TableNoms[I].Nom := Donnees.Nom;
    end;
End;
//-----------------------------------------------------------------------

Maintenant, nous devons trier le tableau par ordre de grandeur de la variable "Nom".

 
Sélectionnez

//-----------------------------------------------------------------------
Procedure TrierParNoms;
Var
   L, C, IdTemp  : Integer;
   ChnTemp,
   Chn1, Chn2    : String;

Begin
   If NbDonnees < 2 Then Exit;
   For L := 0 To NbDonnees - 2 Do
    For C := L + 1 To NbDonnees - 1 Do
     begin
        Chn1 := TableNoms[L].Nom;
        Chn2 := TableNoms[C].Nom;
        If Chn1 > Chn2 Then
         begin
           ChnTemp := TableNoms[L].Nom;
           IdTemp := TableNoms[L].Index;
           TableNoms[L].Nom := TableNoms[C].Nom;
           TableNoms[L].Index := TableNoms[C].Index;
           TableNoms[C].Nom := ChnTemp;
           TableNoms[C].Index := IdTemp;
        end;
    end;
//-----------------------------------------------------------------------



Nous avons trié le tableau, il va falloir maintenant réécrire le fichier dans le bon ordre. Le tableau "TableNoms" est constitué d'un ensemble de mots triés par ordre numérique, et un indice en face de chaque mot ("Nom") correspondant à sa position dans le fichier. La procédure est très simple : Lire le premier élément du tableau TableNoms et récupérer la valeur de la variable Index. Placer ensuite le pointeur de fichier de l'agenda à la position Index. Lire le bloc de données correspondant et écrire ce bloc de données dans un fichier temporaire. Lire l'élément suivant, et ainsi de suite.


 
Sélectionnez

   // Réécriture du fichier
   // Création d'un fichier temporaire
   AssignFile(FichierBak,'FichierTemp');
   If FileExists('FichierTemp') Then DeleteFile('FichierTemp');
   Rewrite(FichierBak);

   // Copier tous les enregistrements du fichier d'origine 
   // dans le fichier temporaire.
   For L := 0 To NbDonnees - 1 Do
    Begin
       // Lecture de la position de l'enregistrement correspondent à "Nom"
       Indice := TableNoms[L].Index;
       // Placer le pointeur de fichier à la position Indice
       Seek(Fichier,Indice);
       // Lire le bloc de données.
       Read(Fichier,Donnees);
       // Ecrire le bloc de données dans le fichier temporaire à 
       // la suite du précédent.
       Write(FichierBak,Donnees);
    end;

   // Fermer les fichier
   CloseFile(FichierBak);
   CloseFile(Fichier);

   // Supprimer le fichier d'origine
   DeleteFile(NomFichier);

   // Renomer le fichier temporaire avec le nom d'origine
   RenameFile('FichierTemp',NomFichier);

End;

Réorganisons la fiche pour une meilleure présentation.

Image non disponible



Il suffit de cliquer sur un nom dans la liste pour charger les données concernant un individu.

Voici notre agenda terminé. Vous pourrez aisément constater qu'en le modifiant sans aucune difficulté, il peut servir de catalogue d'une bibliothèque, de suivi des avancées d'une classe d'élèves ou de répertoire des membres d'un club.

15. Dimensionnement de l'image au format 150 x 150.

Il serait confortable de pouvoir charger des images à des formats d'origine plus grands que ceux imposés par la structure du fichier. Pour cela, nous devons redimensionner l'image pour qu'elle tienne dans un espace de 150 x 150 pixels.

 
Sélectionnez

//------------------------------------------------------------
// Redimensionner l'image au format 150 x 150
//------------------------------------------------------------
Procedure TFichePrincipale.RedimensionneImage;
Var
   BMP     : TBitMap;
   L, H, D : Double;
   X, Y    : Integer;

Begin
   L := Image1.Picture.BitMap.Width;
   H := Image1.Picture.BitMap.Height;
   BMP := TBitMap.Create;
   // Verifier les dimensions de l'image
   If (L > 150) Or (H > 150) Then
    Begin
       // Caluler le facteur de division
       If L > H Then D := L / 150
       Else D := H / 150;
       // Calculer les nouvelles dimensions du BitMap.
       X:=Round(L / D) ;
       Y:=Round(H / D);
    end
   Else
    begin
       BMP.Free;
       Exit;
    end;
   // Attribuer les dimensions correctes au BitMap tampon.
   BMP.Width := X;
   BMP.Height := Y;
   // Copier l'image en la redimensionnant dans le tampon BitMap
   BMP.Canvas.StretchDraw(Rect(0,0,X,Y),Image1.Picture.BitMap);
   // Remplacer l'image redimensionnée.
   Image1.Picture.BitMap.Assign(BMP);id="XVII"
   BMP.Free;
End;

16. Ajoutons une procédure de chargement des images JPEG.

Cela nous apportera plus de confort. La gestion JPEG étant en standard dans DELPHI, la chose n'est pas difficile.

 
Sélectionnez

//------------------------------------------------------------
// Charge une image au forma JPEG et décompresse en BitMap
//------------------------------------------------------------
Procedure TFichePrincipale.ChargeImageJPEG(Nom : String;Image : TImage);
Var
  ImageJPEG   : TJPEGImage;
  ImageBmp    : TBitMap;

Begin
   ImageJPEG:=TJPEGImage.Create;
   Try
     ImageJPEG.LoadFromFile(Nom);
     ImageBmp:=TBitMap.Create;
     Try
       ImageBmp.Assign(ImageJPEG);
       Image.Picture.BitMap.Assign(ImageBmp);
      finally
       ImageBmp.Free;
     end;
    Finally
     ImageJPEG.Free;
   end;
End;
//-------------------------------------------id="XVIII"----------------------------

17. Modification de la procédure de chargement de l'image pour plus de sécurité.

Assurons nous du type de fichier selon sont extension (bmp ou jpg) et redimensionnons l'image si celle-ci dépasse le format imposé.

 
Sélectionnez

procedure TFichePrincipale.BtPhotoClick(Sender: TObject);
Var
   Nom  : String;

begin
   If OpenPictureDialog1.Execute Then
    begin
       Nom := OpenPictureDialog1.FileName;
       If UpperCase(ExtractFileExt(Nom)) = '.JPG' Then
          ChargeImageJPEG(Nom,Image1)
       Else Image1.Picture.LoadFromFile(Nom);
       If (Image1.Picture.Width > 150) Or 
          (Image1.Picture.Height > 150) Then
   id="XIX"     RedimensionneImage;
    end;
end;

18. Conclusion des exercices.

Nous avons vu l'essentiel des routines de gestion de fichier PASCAL, la technique d'utilisation d'une structure pour enregistrer des données dans un fichier et une astuce pour y inclure un BLOB (ici un fichier bitmap). L'aspect de la sécurité a été abordé, mais nous allons quelque peu l'approfondir dans le chapitre suivant.

Je vais poursuivre en parlant de quelques routines très intéressantes et de quelques trucs et astuces concernant l'utilisation des fichiers. Il y a également quelques API WINDOWS dont il est bon de parler.

19. Gestion des fichiers et sécurité.

Que sont les opérations d'entrée/sorties ? Ce sont les opérations de lecture/ecriture de fichiers. Pas seulement les procédure Read, BlockRead, Write, BlockWrite etc .. mais également les fonctions et procédure comme RenameFile, Reset et autre. Si aucune précaution n'a été mise en place, il y aura plantage du programme lors d'une erreur. Il existe une directive de compilation qui permet d'intercepter les erreurs d'opération E/S à l'aide d'une fonction. La directive {$I-} et la fonction IOResult.

Je donne ici un court exemple, vous trouverez une explication détaillée dans l'aide de DELPHI.

 
Sélectionnez

//-----------------------------------------------------------------------
{$I-}
Var
   F      : File;
   Retour : Integer;

Begin
   AssignFile(F,Nom);
   Reset(F);
   Retour := IOResul;
   If Retour > 0 Then
    MessageDlg('Erreur d''entrée/sortie  ' + IntTostr(Retour),
                mtErro,[mbOK],0);
End;
//-----------------------------------------------------------------------



Pour éviter le déclanchement (et la gestion) d'erreur lors d'une tentative d'ouverture d'un fichier, utilisez la fonction FileExits pour vérifier que le fichier existe bien (on peut faire une faute dans le nom).

La fonction DiskFree renvoi l'espace disponible sur le disque de destination.

20. Complément. Quelques fonctions et procédures utiles.

Je ne peux pas énumérer toutes les fonctions et procédures qui ont trait à la gestion des fichiers. Je vous engage donc à consulter l'aide sur l'Unité System, Catégorie : routines d'entrées/sorties.

Pour notre Agenda, vous pouvez avoir besoin de connaître la date de dernière modification. Utilisation de la fonction FileAge :

 
Sélectionnez

//-----------------------------------------------------------------------
Var
   Age              : Integer;
   DateHeureFichier : TDateTime;

Begin
    Age := FileAge(NomFichier);
    DateHeureFichier := FileDateToDateTime(Age);

//-----------------------------------------------------------------------
   
DirectoryExists : permet de créer un nouveau dossier en une seule opération.
ForceDirectories : Pour créer un nouveau fichier.
ChDir : change le répertoire en cours.
CreateDir : ceer un nouveau répertoire. A la différence de ForceDirectories, ChDir ne cré qu'une seule occurence.
GetDir : retourne le nom du repertoire en cours.
ExtractFileName : renvoit le nom seul, d'un fichier si il précédé du chemin.
Append : Pour écrire dans un fichier texte en mode ajout.
ExtractFilePath : renvoit le chemin seul, d'un fichier si il précédé du chemin.

21. Trucs et astuces.

21-1. Connaître la taille en octets de n'importe quel fichier.

Comme il est dit dans l'aide de DELPHI, la fonction FileSize nest pas applicable avec un fichier de type texte (type prédéfini TextFile).

Voici une astuce permettant de connaître la taille réelle en octets de n'importe quel fichier. Il suffit d'utiliser la fonction FileSize en déclarant la variable fichier sans type.

 
Sélectionnez

//-----------------------------------------------------------------------
Function TailleFichier(Nom : String) : Integer;
Var
   F   : File;

Begin
   AssignFile(F,Nom);
   Reset(F,1);
   Result := FileSize(F);
   CloseFile(F);
End;
//-----------------------------------------------------------------------

21-2. Imposer le dossier par défaut de notre agenda au démarrage du programme.

Vous pouvez faire en sorte que lors de l'affichage de la boite de dialogue d'ouverture d'un fichier (notre agenda), le chemin par défaut soit toujours Le même. Supposons que nous ayons créé le dossier devant contenir l'agenda se trouve dans le dossier principal du programme.

 
Sélectionnez

Var
   Principal,
   Dossier     : String;


//Dans la procédure FormCreate de l'application, entrez le code suivant :

Principal := ExtractFileExt(ParamStr(0));
If Principal[Length(Principal)] <> '\' Then Principal := Principal + '\';
Dossier := 'Agenda';
OpenDialog1.InitialDir := Principal + Dossier;

21-3. Empêcher l'effacement accidentel d'un fichier.

La solution est de paramétrer l'attribut du fichier en lecture seule à la fermeture du programme, et de rétablir l'autorisation d'écriture avant l'ouverture du fichier.

 
Sélectionnez

Function  ChangeLectureSeule(Nom : String;LectureSeule : Boolean) : Boolean;
Var
   R,Attr,AttrC : Integer;

Begin
   Attr:=FileGetAttr(Nom);
   If Attr = 0 Then Attr:=faArchive;
   AttrC:=0;
   If (Attr And faHidden = faHidden) Then AttrC:=faHidden;
   If (Attr And faSysFile = faSysFile) Then Attrc:=AttrC + faSysFile;
   If (Attr And faVolumeID = faVolumeID) Then Attrc:=AttrC + faVolumeID;
   If (Attr And faDirectory = faDirectory) Then Attrc:=AttrC + faDirectory;
   If (Attr And faArchive = faArchive) Then Attrc:=AttrC + faArchive;
   If (Attr And faAnyFile = faAnyfile) Then Attrc:=AttrC + faAnyfile;

   If LectureSeule Then AttrC:=AttrC + faReadOnly;
   R:=FileSetAttr(Nom,AttrC);

   Result:=R = 0;
End;

21-4. Si vous voulez changer la date et l'heure de création du fichier.

 
Sélectionnez

Function  ChangeDateHeureFichier(Nom : String;DateHeure : TDateTime) : Boolean;
Var
  H   : Integer;
  DH  : Integer;

Begin
   DH:=DateTimeToFileDate(DateHeure);
   H:=FileOpen(Nom,faAnyFile);
   If H = -1 Then Result:=False
   Else
     begin
        FileSetDate(H,DH);
        FileClose(Hid="");
        Result:=True;
     end;
End;  

22. Le fichier BitMap démystifié.

Je vais en profiter pour démystifier le fichier "Bitmap" (.bmp), et montrer comment on peut charger sont entête pour connaître ses caractéristiques.

22-1. Structure d'un fichier BitMap.

Entête de fichier.

Le fichier BitMap est une suite d'octets représentants les valeurs rouge, vert et bleu de chaque pixel, précédé d'un entête définissant les caractéristiques de l'image. Mais, l'organisation des données est différente selon le type de l'image, du monochrome à 16 millions de couleurs.

Comment sont organisées les données de l'image ?

Après entêtes et table de conversion, commence la série de données représentant l'image. Elles sont organisées en une suite de paquets dont chaque paquet représente une ligne horizontale de l'image. Chaque ligne doit être un multiple de 8. Le premier paquet de données est la dernière ligne, et le dernier paquet représente la première ligne de l'image. Puisque chaque paquet de données doit être un multiple de 8, des octets (neutres) sont ajoutés si la largeur en pixels de l'image n'est pas un multiple de 8.

Image non disponible



Les images comptants 1, 16, 256, 32K ou 64K occupent moins d'espace mais nécessitent une table de conversion.

Image non disponible



Structure de l'entête d'un fichier BitMap. Documentation MicroSoft (Syntaxe C).

typedef struct tagBITMAPFILEHEADER {
  WORD bfType;
  DWORD bfSize;
  WORD bfReserved1;
  WORD bfReserved2;
  DWORD bfOffBits;
} BITMAPFILEHEADER;

L'entête du fichier fait donc 14 octets.

Structure de l'entête d'un BitMap. Documentation MicroSoft (Syntaxe C).

typedef struct tagBITMAPINFOHEADER{
   DWORD biSize;
   LONG biWidth;
   LONG biHeight;
   WORD iplanes;
   WORD biBitCount
   DWORD biCompression;
   DWORD biSizeImage;
   LONG biXPelsPerMeter;
   LONG biYPelsPerMeter;
   DWORD biClrUsed;
   DWORD biClrImportant;
} BITMAPINFOHEADER;


L'entête de définition du BitMap fait 40 octets (soit un total de 54).

22-2. Exemple d'une palette de conversion en 256 couleurs.

Image non disponible


22-3. Programme permettant de connaître les caractéristiques d'une image BitMap sans charger tout le fichier.

Voici un petit programme qui se contente de lire l'entête d'un fichier BitMap (.bmp) et d'afficher ses caractéristiques.

NB : Je l'ai complété d'un objet TImage et du chargement de l'image en quesion uniquement pour avoir une visualisation du fichier en question.

Source ProjetBitMap1.zip

23. Liste des codes d'erreurs Entrées/Sorties DELPHI et WINDOWS

Les codes d'erreurs E/S DELPHI sont retournés par la fonction IOResult.
ATTENTION : La directive de compilation $I doit être paramétrer en conséquence.

Codes DELPHI Messages   Codes WINDOWS Messages Traductions
2 Fichier non trouvé   2 ERROR_File_NOT_FOUND  
3 Chemin non trouvé   3 ERROR_PATH_NOT_FOUND  
4 Trop de fichiers ouverts   4 ERROR_TOO_MANY_OPEN_FILES  
5 Accès au fichier refusé   5 ERROR_ACCESS_DENIED  
6 Identificateur de fichier invalide   6 ERROR_INVALID_HANDLE  
12 Code d'accès au fichier invalide   12 ERROR_INVALID_ACCESS  
15 Numéro d'unité de disque invalide   15 ERROR_INVALID_DRIVE  
16 Ne peut supprimer le répertoire courant   16 ERROR_CURRENT_DIRECTORY  
17 Ne peut renommer de disque à disque   17 ERROR_NOT_SAME_DEVICE  
100 Erreur de lecture   30 ERROR_READ_FAULT  
101 Erreur d'écriture   29 ERROR_WRITE_FAULT  
102 Variable non asignée à un fichier        
103 Fichier non ouvert   110 ERROR_OPEN_FAILED  
104 Fichier non ouvert en lecture        
105 Fichier non ouvert en écriture        
106 Format numérique invalide   1006 ERROR_FILE_INVALID  
150 Disque protégé en écriture   108 ERROR_DRIVE_LOCKED  
151 Unité de disque non reconnue par le système   20 ERROR_BAD_UNIT  
152 Unité de disque non prète        
153 Commande inconnue        
154 Erreur d'intégrité des données lues        
155 Disque spécifié invalide        
156 Erreur de positionnement des tête de lecture        
157 Type de média invalide        
158 Secteur non trouvé   27 ERROR_SECTOR_NOT_FOUND  
159 Erreur imprimante- plus de papier        
160 Erreur d'écriture sur le périphérique        
161 Erreur de lecture sur le périphérique        
162 Erreur lié au matériel   18 ERROR_NO_MORE_FILES  
      19 ERROR_WRITE_PROTECT Fichier protégé en écriture (lecture seule)
      26 ERROR_NOT_DOS_DISK Disque non DOS
      80 ERROR_FILE_EXISTS Le fichier existe déjà
      107 ERROR_DISK_CHANGE Erreur lors d'un hangement de disque
      112 ERROR_DISK_FULL Disque plein
      161 ERROR_BAD_PATHNAME Mauvais chemin
      266 ERROR_CANNOT_COPY Copie impossible
      267 ERROR_DIRECTORY Erreur de répertoire
      2401 ERROR_OPEN_FILES Erreur d'ouverture de fichier
           

24. Sources des exercices.

Sources au format ZIP :

-A- Sources des exercices
-B- Editeur CSV
-C- Projet BitMap
-D- Fichier exemple d'un agenda (pour notre didacticiel).
      ATTENTION : Cet agenda d'exemple n'est compatible qu'avec les exercices 4 et 5.

25. Liens divers et bibliographie

26. Que réserve la seconde partie du tutoriel ?

Tout d'abord, une autre technique pour intégrer dans notre agenda n'importe quel type de donnée, en dehors de la structure. La photo sera ajoutée à la suite, et un RichTextFile à la suite de la photo.

Je parlerais également des API WINDOWS, des Handles de fichier et de diverses choses.

L'édition Hexadécimale sera abordée également.

27. L'article complet au format PDF

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2006 Philippe GORMAND. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.