I. 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 ont été conservés. 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 CD-ROM).
Bien sûr, la notion de fichier a énormément évolué. 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.
II. 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émoires 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 CD-ROM 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 CD-ROM ou DVD. C'est un disque optique ne contenant qu'une seule piste en spirale. Un CD-ROM 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.
Je ne parlerai 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'écriture 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 ?
Trois parties les composent. Le secteur de Boot, la table d'allocation de fichiers 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 fichiers 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 ?
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 (système, 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 CD-ROM, c'est un fichier en début de piste qui tient lieu de table d'allocation de fichiers.
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 s'il existe.
À 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.
III. Les différents formats de fichiers et leur extension▲
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 texte et les fichiers binaires. On parle de format, de fichier typé et de fichier non typé. Nous verrons progressivement le 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 parmi 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és « binaire », car leur utilisation nécessitait 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.
III-A. 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 chaine. À 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 chaine 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 chaine réceptrice.
III-B. Fichier binaire▲
On appelle 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, etc.
III-C. 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 fichiers ayant la même structure (du point vue informatique) que le fichier texte, mais utilisés de façon particulière. Les fichiers CSV, INI et HTML.
III-C-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. À la lecture, il suffit de compter le nombre de points-virgules dans la chaine 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
III-C-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 tutoriels sur le sujet et de bons exemples dans la FAQ DELPHI du site.
III-C-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és de balises (des mots réservés au langage HTML) qui les feront apparaître avec une taille, un style et une couleur définis 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.
III-D. 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
saurais 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.
III-E. 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étafichier (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;
III-F. 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éfini 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.
IV. 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'ont prévu.
IV-A. 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 fichiers (sur le disque) les informations qui permettront son 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. N. B. 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ées par DELPHI. On peut donc les utiliser (a 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 choisi 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 tutoriel. 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 s’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.
IV-B. Les principales routines de gestion de fichiers 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 connaître 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 |
Renvoie 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).
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 informations dans la table d'allocation de fichiers, et non dans le fichier lui-même. FileGetAttr utilise la fonction GetFileAttributes implémentée dans l'unité WINDOWS, c'est une API.
Ce chapitre est sans doute un peu difficile, mais il expose des notions fondamentales sur la gestion des fichiers sous environnement WINDOWS.
V. 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ées peut être un type prédéfini par le langage ou bien un type de données défini, 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é.
V-A. Déclaration d'un identifiant de fichier▲
Déclaration d'un fichier non typé.
Var
Fichier : File
;
Déclaration d'un identifiant de fichier selon un type prédéfini.
Var
Fichier : File
Of
Integer
;
Un type fichier prédéfini, le type fichier texte.
Var
Fichier : TextFile;
Éclaircissement 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éfini 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.
N. B. 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.
V-B. Les structures dans les fichiers▲
Déclaration d'un identifiant de fichier selon un type défini pour un besoin.
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.
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.
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
;
V-C. 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.
N. B. 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.
Exemple de définition d'un fichier à structure fixe.
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 variable « Donnees » dont l'identificateur est défini, donc sa taille est connue : 41 + 41 + 81 + 21 + 4 = 188 octets. La fonction FileSize renvoie le nombre d'enregistrements selon le type de fichier défini et non pas le nombre d'octets qu'il occupe sur le disque. Le fichier étant du type TDonnees qui fait 188 octets, si le fichier occupe 188 octets il y a 1 seul enregistrement : FileSize renverra 1.
VI. Les types String et AnsiString dans les structures▲
Le type AnsiString est le type de chaine de caractères 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 ? À l'origine du Turbo-Pascal, les machines travaillaient en 8 puis 16 bits. Le type string était un tableau prédéfini de 255 caractères. Le caractère zéro (un octet) était utilisé pour indiquer la longueur de la chaine (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 chaines de caractères. Le type ShortString peut être redéfini dans sa longueur. Exemple :
Var
Chaine1 : ShortString;// chaine de 255 caractères
Chaine2 : ShortString[100
] ;// chaine 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 :
Var
Chaine1 : Array
[0
..255
] Of
Char
;// ShortString
Chaine2 : ^Char
; // AnsiString
Donc, si nous écrivons le code suivant :
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 chaine
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.
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 chaine
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.
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 chaine
Chaine2 := 'Je vous salue tous'
;
C := Chaine2[4
]; // C = 'v'
Mais, le compilateur aura traduit le code de la façon suivante :
begin
C := Chaine1[8
]; // récupère le huitième caractère de la chaine
GetMem(Chaine2,SizeOf('Je vous salue tous'
));
Chaine2 := 'Je vous salue 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.
VI-A. Pourquoi avoir conservé l'ancien type String et créé des automatismes sur l'usage des pointeurs ?▲
1° pour assurer une compatibilité ascendante.
2° pour simplifier le travail du développeur.
Mais il y a d'autres raisons, complémentaires.
3° Dans le cas de travail avec des chaines 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).
4° Il n'est pas possible de travailler avec une structure (Record) de chaines 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 :
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; indiquant [Erreur] Unit1.pas(24): Le type 'TChaine' nécessite une finalisation - non autorisé dans type fichier
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.
type
TF = Record
Tab : Array
Of
Array
Of
Integer
;
End
;
Var
F : File
Of
TF;
Provoque une erreur de compilation.
VII. 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é, système, ou en lecture seule.
On déclare le fichier à copier comme étant non typé et on fait appel aux procédures BlockRead et BlockWrite.
//---------------------------------------------------------------
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'erreurs 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);
// Écrire dans le destinataire autant de données lues.
BlockWrite(FD,Buf,NL,NE);
// Si une erreur est détectée, 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éseau, excepté les supports en lecture seule comme les CD-ROM bien sûr.
VIII. 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. À la lecture, la procédure ReadLn lira autant de données (d'octets) jusqu’à 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.
Lecture et écriture d'un fichier texte.
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 chaine 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 zéro.
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.
IX. 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.
X. 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.
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);
// Écrire 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 correspondants à 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 sûr, nous avons besoin d'une interface utilisateur (aussi appelé Interface Homme/Machine) pour saisir et afficher les données.
X-A. Mettons en pratique le mécanisme, avec une saisie, une navigation et un enregistrement des données▲
Exercice -1-
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 chaine 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 un 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 sinon 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.
//-----------------------------------------------------------------------
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 renvoie le nombre d'enregistrements selon son type
// défini et non pas le nombre d'octets qu'il occupe sur le disque.
// Le fichier étant du type TDonnees qui fait 184 octets, 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 recherche.
//-----------------------------------------------------------------------
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.
//-----------------------------------------------------------------------
// 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.
//-----------------------------------------------------------------------
// 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
.
X-B. 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.
Ajoutons une variable pour le fichier temporaire.
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.
//-----------------------------------------------------------------------
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);
// Renommer 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 sûr être grandement amélioré et sécurisé. Nous allons aborder maintenant la notion de flux.
XI. 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'eau 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 chaines 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).
XII. 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 structures identiques, ou ne devant pas dépasser une dimension prévue. 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éfini avec une dimension suffisante. Si l'image est en 16 millions de pixels, elle occupera (en format 32 bits) 90 000 octets plus l'entête définissant l'image soit un total de 90 054 octets. Nous allons devoir passer par un flux pour charger et stocker l'image dans la structure.
Exercice 3.
Modification de la structure.
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.
//-----------------------------------------------------------------------
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);
// connaître 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;
// À ce stade, le flux n'a pas d'espace réservé. Il faut donc lui
// attribuer 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.
XIII. 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.
Complément de la structure avec une chaine de caractères qui contiendra le commentaire.
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.
//-----------------------------------------------------------------------
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 chaine 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"------
XIV. 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 chaines de caractères, ce n'est pas la longueur de la chaine que nous obtenons, mais la valeur numérique de la somme des caractères.
La chaine « ABC » est plus petite que la chaine « 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 chaine 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 chaines 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éfinie selon le nombre d'enregistrements de l'agenda.
Exercice 5.
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.
//-----------------------------------------------------------------------
Procedure
ChargeNomsAgenda;
Var
I : Integer
;
Begin
// Définition de la taille du tableau selon le nombre d'enregistrements
SetLength(TableNoms,NbDonnees);
// Plaçons 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 ».
//-----------------------------------------------------------------------
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.
// 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);
// Écrire le bloc de données dans le fichier temporaire à
// la suite du précédent.
Write
(FichierBak,Donnees);
end
;
// Fermer les fichiers
CloseFile(FichierBak);
CloseFile(Fichier);
// Supprimer le fichier d'origine
DeleteFile(NomFichier);
// Renommer le fichier temporaire avec le nom d'origine
RenameFile('FichierTemp'
,NomFichier);
End
;
Réorganisons la fiche pour une meilleure présentation.
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.
XV. 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.
//------------------------------------------------------------
// 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;
// Vérifier les dimensions de l'image
If
(L > 150
) Or
(H > 150
) Then
Begin
// Calculer 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
;
XVI. 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.
//------------------------------------------------------------
// 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"----------------------------
XVII. Modification de la procédure de chargement de l'image pour plus de sécurité▲
Assurons-nous du type de fichier selon son extension (bmp ou jpg) et redimensionnons l'image si celle-ci dépasse le format imposé.
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
;
XVIII. Conclusion des exercices▲
Nous avons vu l'essentiel des routines de gestion de fichiers 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.
XIX. Gestion des fichiers et sécurité▲
Que sont les opérations d'entrées/sorties ? Ce sont les opérations de lecture/écriture de fichiers. Pas seulement les procédures Read, BlockRead, Write, BlockWrite, etc., mais également les fonctions et procédures 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.
//-----------------------------------------------------------------------
{$I-}
Var
F : File
;
Retour : Integer
;
Begin
AssignFile(F,Nom);
Reset(F);
Retour := IOResul;
If
Retour > 0
Then
MessageDlg('Erreur d''entrée/sortie N° '
+ IntTostr(Retour),
mtErro,[mbOK],0
);
End
;
//-----------------------------------------------------------------------
Pour éviter le déclenchement (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 renvoie l'espace disponible sur le disque de destination.
XX. 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 :
//-----------------------------------------------------------------------
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 |
crée un nouveau répertoire. À la différence de ForceDirectories, ChDir ne crée qu'une seule occurrence. |
GetDir |
retourne le nom du répertoire en cours. |
ExtractFileName |
renvoie le nom seul, d'un fichier s'il est précédé du chemin. |
Append |
pour écrire dans un fichier texte en mode ajout. |
ExtractFilePath |
renvoie le chemin seul, d'un fichier s'il est précédé du chemin. |
XXI. Trucs et astuces▲
XXI-A. Connaître la taille en octets de n'importe quel fichier▲
Comme il est dit dans l'aide de DELPHI, la fonction FileSize n’est 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.
//-----------------------------------------------------------------------
Function
TailleFichier(Nom : String
) : Integer
;
Var
F : File
;
Begin
AssignFile(F,Nom);
Reset(F,1
);
Result := FileSize(F);
CloseFile(F);
End
;
//-----------------------------------------------------------------------
XXI-B. 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.
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;
XXI-C. 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.
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
;
XXI-D. Si vous voulez changer la date et l'heure de création du fichier▲
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
;
XXII. Le fichier Bitmap démystifié▲
Je vais en profiter pour démystifier le fichier « Bitmap » (.bmp), et montrer comment on peut charger son entête pour connaître ses caractéristiques.
XXII-A. Structure d'un fichier Bitmap▲
Entête de fichier.
Le fichier Bitmap est une suite d'octets représentant 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ête 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.
Les images comptant 1, 16, 256, 32 K ou 64 K occupent moins d'espace, mais nécessitent une table de conversion.
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).
XXII-B. Exemple d'une palette de conversion en 256 couleurs▲
XXII-C. 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.
N.B. Je l'ai complété d'un objet TImage et du chargement de l'image en question uniquement pour avoir une visualisation du fichier en question.
Source ProjetBitMap1.zip
XXIII. 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étré 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 assigné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êtes 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ée 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 changement 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 |
|||
XXIV. 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.
XXV. Liens divers et bibliographie▲
-1- Gestion de fichier FAQ Developpez.com
-2- DELPHI SOURCES Developpez.com, fichiers
-3- FICHIERS INI FAQ Developpez.com
-4- Les programmes en code source des livres de Jhon Colibri
XXVI. 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ées, en dehors de la structure. La photo sera ajoutée à la suite, et un RichTextFile à la suite de la photo.
Je parlerai également des API WINDOWS, des Handles de fichier et de diverses choses.
L'édition hexadécimale sera abordée également.