![]() |
||||||||||||||||||||||||||
Préambule : le Codage 10. Fichiers
Organisation des fichiers
11. Procédures et Fonctions |
« On ne peut pas
davantage créer des fichiers numériques non copiables que créer de l’eau non
humide »
Jusqu’à présent,
les informations utilisées dans nos programmes ne pouvaient provenir que de
deux sources : soit elles étaient inclues dans l’algorithme lui-même, par le
programmeur, soit elles étaient entrées en cours de route par l’utilisateur.
Mais évidemment, cela ne suffit pas à combler les besoins réels des
informaticiens.
|
|
Fichiers Texte | Fichiers Binaires |
On les utilise pour stocker... | des bases de données | tout, y compris des bases de données. |
Ils sont structurés sous forme de... | lignes (enregistrements) | Ils n'ont pas de structure apparente. Ce sont des octets écrits à la suite les uns des autres. |
Les données y sont écrites... | exclusivement en tant que caractères | comme en mémoire vive |
Les enregistrements sont eux-mêmes structurés... | au choix, avec un séparateur ou en champs de largeur fixe | en champs de largeur fixe, s'il s'agit d'un fichier codant des enregistrements |
Lisibilité | Le fichier est lisible clairement avec n'importe quel éditeur de texte | Le fichier a l'apparence d'une suite d'octets illisibles |
Lecture du fichier | On ne peut lire le fichier que ligne par ligne | On peut lire les octets de son choix (y compris la totalité du fichier d'un coup) |
Dans le cadre de ce
cours, on se limitera volontairement au type de base : le fichier texte en
accès séquentiel. Pour des informations plus complètes sur la gestion des
fichiers binaires et des autres types d'accès, il vous faudra... chercher
ailleurs.
4. Instructions (fichiers texte en accès séquentiel)
Si l’on veut
travailler sur un fichier, la première chose à faire est de l’ouvrir.
Cela se fait en attribuant au fichier un numéro
de canal. On ne peut ouvrir qu’un seul fichier par canal, mais
quel que soit le langage, on
dispose toujours de plusieurs canaux, donc pas de soucis.
L’important est
que lorsqu’on ouvre un fichier, on stipule ce qu’on va en faire : lire,
écrire ou ajouter.
Au premier abord,
ces limitations peuvent sembler infernales. Au deuxième rabord, elles le
sont effectivement. Il n'y a même pas d'instructions qui permettent de
supprimer un enregistrement d'un fichier !
Toutefois, avec un peu d’habitude, on se rend compte que
malgré tout, même si ce n’est pas toujours marrant, on peut quand même faire
tout ce qu’on veut avec ces fichiers séquentiels.
Pour ouvrir un
fichier texte, on écrira par exemple :
Ouvrir “Exemple.txt” sur 4 pour Lecture
Ici, “Exemple.txt” est le nom du fichier sur le disque dur, 4 est le numéro de canal, et ce fichier a donc été ouvert en lecture. Vous l’aviez sans doute pressenti. Allons plus loin :
Ouvrir
“Exemple.txt” sur 4 en Lecture
Variables
Truc, Nom, Prénom, Tel, Mail en Caractères
...
LireFichier
4, Truc
Nom
←
Mid(Truc, 1, 20)
Prénom ←
Mid(Truc, 21, 15)
Tel ←
Mid(Truc, 36, 10)
Mail ←
Mid(Truc, 46, 20)
L’instruction
LireFichier récupère donc dans la variable spécifiée l’enregistrement suivant dans
le fichier... "suivant", oui, mais par rapport à quoi ? Par rapport au dernier
enregistrement lu. C’est en cela que le fichier est dit séquentiel. En
l’occurrence, on récupère donc la première ligne, donc, le premier
enregistrement du fichier, dans la variable Truc. Ensuite, le fichier étant
organisé sous forme de champs de largeur fixe, il suffit de tronçonner cette
variable Truc en autant de morceaux qu’il y a de champs dans
l’enregistrement, et d’envoyer ces tronçons dans différentes variables. Et
le tour est joué.
La suite du
raisonnement s’impose avec une logique impitoyable : lire un fichier
séquentiel de bout en bout suppose de programmer une boucle. Comme on
sait rarement à l’avance combien d’enregistrements comporte le fichier, la
combine consiste neuf fois sur dix à utiliser la fonction EOF (acronyme pour
End Of File). Cette fonction renvoie la valeur Vrai si on a atteint la fin
du fichier (auquel cas une lecture supplémentaire déclencherait une erreur).
L’algorithme, ultra classique, en pareil cas est donc :
Variable
Truc en Caractère
Ouvrir
“Exemple.txt” sur 5 en Lecture
Tantque Non
EOF(5)
LireFichier 5,
Truc
…
FinTantQue
Et neuf fois sur
dix également, si l’on veut stocker au fur et à mesure en mémoire vive les
informations lues dans le fichier, on a recours à des tableaux. Et comme on
ne savait pas d’avance combien il y aurait d’enregistrements dans le
fichier, on ne sait pas davantage combien il doit y avoir d’emplacements
dans les tableaux. Qu’importe, les programmeurs avertis que vous êtes
connaissent la combine des tableaux dynamiques.
En rassemblant
l’ensemble des connaissances acquises, nous pouvons donc écrire le prototype
de lecture intégrale d’un fichier séquentiel, lecture qui recopie l’ensemble
des informations en mémoire vive :
Tableaux
Nom(), Prénom(), Tel(), Mail() en Caractère
Ouvrir
“Exemple.txt” sur 5 en Lecture
i
←
-1
Tantque Non
EOF(5)
LireFichier 5,
Truc
i
←
i + 1
Redim Nom(i)
Redim Prénom(i)
Redim Tel(i)
Redim Mail(i)
Nom(i) ←
Mid(Truc, 1, 20)
Prénom(i)
←
Mid(Truc, 21, 15)
Tel(i) ←
Mid(Truc, 36, 10)
Mail(i) ←
Mid(Truc, 46, 20)
FinTantQue
Ici, on a fait le
choix de recopier le fichier dans quatre tableaux distincts. Comme on va le
voir bientôt, il y a encore mieux, mais ne nous impatientons pas, chaque
chose en son temps.
Pour une
opération d’écriture, ou d’ajout, il faut d’abord constituer une chaîne
équivalente à la nouvelle ligne du fichier. Cette chaîne doit donc être
« calibrée » de la bonne manière, avec les différents champs qui « tombent »
aux emplacements corrects. Le moyen le plus simple pour s’épargner de longs
traitements est de procéder avec des chaînes correctement dimensionnées dès
leur déclaration (la plupart des langages offrent cette possibilité) :
Ouvrir
“Exemple.txt” sur 3 en Ajout
Variable
Truc en Caractère
Variables
Nom*20, Prénom*15, Tel*10, Mail*20 en Caractère
une telle déclaration assure que quel que soit le contenu de la variable
Nom, par exemple, celle-ci comptera toujours 20 caractères. Si son contenu
est plus petit, alors un nombre correct d’espaces sera automatiquement
ajouté pour combler. Si on tente d’y entrer un contenu trop long, celui-ci
sera automatiquement tronqué.
…
Nom ←
“Jokers“
Prénom ←
“Midnight”
Tel ←
“0348946532”
Mail
←
“allstars@rockandroll.com”
Truc
←
Nom & Prénom & Tel & Mail
EcrireFichier
3, Truc
Et pour finir, une fois qu’on en a terminé avec un fichier, il ne faut pas oublier de fermer ce fichier. On libère ainsi le canal qu’il occupait (et accessoirement, on pourra utiliser ce canal dans la suite du programme pour un autre fichier… ou pour le même).
Il existe globalement deux manières de traiter les fichiers textes :
Les avantages de la seconde technique sont nombreux, et 99 fois sur 100, c'est ainsi qu'il faudra procéder :
Pourquoi, alors, demanderez-vous haletants, ne fait-on pas cela à tous les coups ? Y a-t-il des cas où il vaut mieux en rester aux fichiers et ne pas passer par des tableaux ?
6.1 Données structurées simples
Nostalgiques du
Lego, cette partie va vous plaire. Comment construire des trucs pas
possibles et des machins pas croyables avec juste quelques éléments de base
? Vous n'allez pas tarder à le savoir...
Jusqu'à présent, voilà comment se présentaient nos possibilités en matière
de mémoire vive : nous pouvions réserver un emplacement pour une
information, d'un certain type. Un tel emplacement s'appelle une variable
(quand vous en avez assez de me voir radoter, vous le dites). Nous pouvions aussi
réserver une série d'emplacement numérotés pour une série d'information de
même type. Un tel emplacement s'appelle un tableau (même remarque).
Eh bien toujours
plus haut, toujours plus fort, voici maintenant que nous pouvons réserver
une série d'emplacements pour des données de type différents. Un tel
emplacement s'appelle une variable structurée.
Son utilité, lorsqu'on traite des fichiers texte (et même, des fichiers en
général), saute aux yeux : car on va pouvoir calquer chacune des lignes
du fichier en mémoire vive, et considérer que chaque enregistrement sera
recopié dans une variable et une seule, qui lui sera adaptée. Ainsi,
le problème du "découpage" de chaque enregistrement en différentes variables
(le nom, le prénom, le numéro de téléphone, etc.) sera résolu d'avance,
puisqu'on aura une structure, un gabarit, en quelque sorte, tout prêt
d'avance pour accueillir et prédécouper nos enregistrements.
Attention toutefois ; lorsque nous utilisions des variables de type
prédéfini, comme des entiers, des booléens, etc. nous n'avions qu'une seule
opération à effectuer : déclarer la variable en utilisant un des types
existants. A présent que nous voulons créer un nouveau type de variable (par assemblage
de types existants), il va falloir faire deux choses : d'abord, créer le
type. Ensuite seulement, déclarer la (les) variable(s) d'après ce type.
Reprenons une fois de plus l'exemple du carnet d'adresses. Je sais, c'est un
peu comme mes blagues, ça lasse (là, pour ceux qui s'endorment, je signale
qu'il y a un jeu de mots), mais c'est encore le meilleur moyen d'avoir un
point de comparaison.
Nous allons donc, avant même la déclaration des variables, créer un type,
une structure, calquée sur celle de nos enregistrements, et donc prête à les
accueillir :
Structure
Bottin
Nom en Caractère * 20
Prénom en Caractère
* 15
Tel en caractère * 10
Mail en Caractère * 20
Fin Structure
Ici, Bottin est le nom de ma structure. Ce mot jouera par la suite dans mon programme exactement le même rôle que les types prédéfinis comme Numérique, Caractère ou Booléen. Maintenant que la structure est définie, je vais pouvoir, dans la section du programme où s'effectuent les déclarations, créer une ou des variables correspondant à cette structure :
Variable Individu en Bottin
Et si cela me chantait, je pourrais remplir les différentes informations contenues au sein de la variable Individu de la manière suivante :
Individu ←“Joker“,“Midnight”,“0348946532”,“allstars@rock.com”
On peut aussi avoir besoin d'accéder à un seul des champs de la variable structurée. Dans ce cas, on emploie le point :
Individu.Nom
←
“Jokers“
Individu.Prénom ←
“Midnight”
Individu.Tel ←
“0348946532”
Individu.Mail
←
“allstars@rockandroll.com”
Ainsi, écrire correctement une information dans le fichier est un jeu d'enfant, puisqu'on dispose d'une variable Individu au bon gabarit. Une fois remplis les différents champs de cette variable - ce qu'on vient de faire -, il n'y a plus qu'à envoyer celle-ci directement dans le fichier. Et zou !
EcrireFichier 3, Individu
De la même manière, dans l'autre sens, lorsque j'effectue une opération de lecture dans le fichier Adresses, ma vie en sera considérablement simplifiée : la structure étant faite pour cela, je peux dorénavant me contenter de recopier une ligne du fichier dans une variable de type Bottin, et le tour sera joué. Pour charger l'individu suivant du fichier en mémoire vive, il me suffira donc d'écrire :
LireFichier 5, Individu
Et là, direct,
j'ai bien mes quatre renseignements accessibles dans les quatre champs de la
variable individu. Tout cela, évidemment, parce que la structure de ma
variable Individu correspond parfaitement à la structure des enregistrements
de mon fichier. Dans le cas contraire, pour reprendre une expression connue,
on ne découpera pas selon les pointillés, et alors, je pense que vous
imaginez le carnage...
6.2 Tableaux de données structurées
Et encore plus loin, encore plus vite et encore plus fort. Si à partir des
types simples, on peut créer des variables et des tableaux de variables,
vous me voyez venir, à partir des types structurés, on peut créer des
variables structurées... et des tableaux de variables structurées.
Là, bien que pas si difficile que cela, ça commence à devenir vraiment balèze. Parce que cela veut dire que nous
disposons d'une manière de gérer la mémoire vive qui va correspondre
exactement à la structure d'un fichier texte (d'une base de données). Comme
les structures se correspondent parfaitement, le nombre de manipulations à
effectuer, autrement dit de lignes de programme à écrire, va être réduit au
minimum. En fait, dans notre tableau structuré, les champs des emplacements
du tableau correspondront aux champs du fichier texte, et les indices des
emplacements du tableaux correspondront aux différentes lignes du fichier.
Voici, à titre d'illustration, l'algorithme complet de lecture du fichier
Adresses et de sa recopie intégrale en mémoire vive, en employant un tableau
structuré.
Structure
Bottin
Nom en Caractère * 20
Prénom en Caractère
* 15
Tel en caractère * 10
Mail en Carctère * 20
Fin Structure
Tableau
Mespotes() en Bottin
Ouvrir
“Exemple.txt” sur 3 en Ajout
i
←
-1
Tantque Non
EOF(5)
i
←
i + 1
Redim Mespotes(i)
LireFichier
5, Mespotes(i)
FinTantQue
Une fois que ceci est réglé, on a tout ce qu'il faut ! Si je voulais écrire, à un moment, le mail de l'individu n°13 du fichier (donc le n°12 du tableau) à l'écran, il me suffirait de passer l'ordre :
Ecrire Mespotes(12).Mail
Et voilà le
travail. Simplissime, non ?
REMARQUE FINALE SUR LES DONNEES STRUCTUREES
Même si le domaine de prédilection des données structurées est la gestion de
fichiers, on peut tout à fait y avoir recours dans d'autres contextes, et
organiser plus systématiquement les variables d'un programme sous la forme
de telles structures. |
Lorsqu'on est amené à travailler avec des données situées dans un fichier,
plusieurs choix, en partie indépendants les uns des autres, doivent être
faits :
●
sur l'organisation en enregistrements
du fichier (choix entre fichier texte ou
fichier binaire)
●
sur le mode d'accès aux enregistrements
du fichier (direct ou
séquentiel)
●
sur l'organisation des champs au sein
des enregistrements (présence de séparateurs ou
largeur fixe)
●
sur la méthode de traitement des
informations (recopie intégrale du fichier en mémoire vive ou non)
●
sur le type de variables utilisées pour
cette recopie en mémoire vive (plusieurs tableaux de type
simple, ou un seul tableau de type
structuré).
Chacune de ces
options présente avantages et inconvénients, et il est impossible de donner
une règle de conduite valable en toute circonstance. Il faut connaître ces
techniques, et savoir choisir la bonne option selon le problème à traiter.
Voici une série de (pas toujours) petits exercices sur les fichiers texte, que
l'on pourra traiter en employant les types structurés (c'est en tout cas le
cas dans les corrigés).
REMARQUE N°1
Lorsqu'on veut récupérer des données
numériques inscrites dans un fichier texte, il ne faut surtout pas oublier
que ces données se présentent forcément sous forme de caractères. La
récupération elle-même transmettra donc obligatoirement des données de type
alphanumérique ; pour utiliser ces données à des fins ultérieures de calcul,
il sera donc nécessaire d'employer une fonction de conversion. |
REMARQUE N°1bis Voilà pourquoi une structure s'appliquant aux fichiers textes est forcément composée uniquement de types caractères. Une structure traitant de fichiers binaires pourrait en revanche être composée de caractères, de numériques et de booléens. |
REMARQUE N°2
Plusieurs langages interdisent l'écriture d'une variable structurée dans un
fichier texte, ne l'autorisant que pour un fichier binaire. |