Cours pascal
Par
Nabster (hello world) (Blog)
V. Tableaux
Les tableaux permettent de manipuler plusieurs
informations de même type, de leur mettre un indice : la 1ère info,
la 2ème info,..., la ième info, . . .
Ils sont stockés en mémoire centrale comme les autres
variables, contrairement aux fichiers qui sont stockés sur le
disque.
Une propriété importante des tableaux est de permettre
un accès direct aux données, grâce à l'indice.
| On appelle souvent vecteur un tableau en une dimension. |
V.1. Le type array
V.1.1. Principe
Syntaxe
I étant un type intervalle, et T
un type quelconque.
Ce type définit un tableau comportant un certain nombre de cases
de type
T , chaque case est repérée par un indice de type I.
Exemple
TYPE vec_t = array [1 ..10 ] of integer ;
VAR v : vec_t;
|
v est un tableau de 10 entiers, indicés de 1 à 10.
- À la déclaration, le contenu du tableau est indéterminé,
comme toute variable.
- On accède à la case indice i par v[i] (et non v(i)).
- Pour mettre toutes les cases à 0 on fait
for i := 1 to 10 do
v[i] := 0 ;
|
Remarque : L'intervalle du array peut être de tout
type intervalle, par exemple 1..10, 'a'..'z', false..true, ou
encore un intervalle d'énumérés Lundi..Vendredi.
On aurait pu déclarer vecteur comme ceci (peu
d'intérêt) :
TYPE interv = 1 ..10 ;
vec_t = array [ interv ] of integer ;
|
V.1.2. Contrôle des bornes
Il est en général conseillé de repérer les bornes de
l'intervalle avec des constantes nommées : si on décide de changer
une borne, cela est fait à un seul endroit dans le programme.
L'écriture préconisée est donc
CONST vec_min = 1 ;
vec_max = 10 ;
TYPE vec_t = array [vec_min..vec_max] of integer ;
|
Règle 1
Il est totalement interdit d'utiliser un indice en dehors de
l'intervalle de déclaration, sinon on a une erreur à l'exécution.
Il faut donc être très rigoureux dans le programme, et
ne pas hésiter à tester si un indice i est correct avant de se
servir de v[i].
Exemple : Programme demandant à rentrer une
valeur dans le vecteur.
CONST vec_min = 1 ;
vec_max = 10 ;
TYPE vec_t = array [vec_min..vec_max] of integer ;
VAR v : vect_t;
i : integer ;
BEGIN
write (' i ? ' );
readln(i);
if (i >= vec_min) and (i <= vec_max) then
begin
write (' v[ ' , i, ' ] ? ' );
readln(v[i]);
end
else
writeln(' Erreur, i hors intervalle ' , vec_min, ' .. ' , vec_max);
END .
|
Règle 2
Le test d'un indice i et de la valeur en cet indice v[i] dans
la même expression sont interdits.
Exemple :
if (i >= vec_min) and (i <= vec_max) and (v[i] <> -1 ) then
...
else
...;
|
Une expression est toujours évaluée en intégralité;
donc si (i <= vec_max), le test (v[i] <> -1)
sera quand même effectué, alors même que l'on sort du vecteur !
Solution : séparer l'expression en 2.
if (i >= vec_min) and (i <= vec_max) then
if (v[i] <> -1 ) then
...
else
...
else
... ;
|
V.1.3. Recopie
En Pascal, la seule opération globale sur un tableau est :
recopier le contenu d'un tableau v1 dans un tableau v2 en écrivant : v2 := v1;
Ceci est équivalent (et plus efficace) que
for i := vec_min to vec_max do
v2[i] := v1[i];
|
Il y a une condition : les 2 tableaux doivent être exactement de mêmes types,
i.e issus de la même déclaration.
TYPE vecA = array [1 ..10 ] of char ;
vecB = array [1 ..10 ] of char ;
VAR v1 : vecA;
v2 : vecA;
v3 : vecB;
BEGIN
v2 := v1;
v3 := v1;
|
V.2. Super tableaux
Quelques types un peu plus complexes à base de tableaux,
et de combinaisons entre types.
V.2.1. Tableaux à plusieurs dimensions
Exemple : dimension 1 = vecteur ; dimension 2 = feuille excel ;
dimension 3 =classeur excel.
On peut créer des tableaux à plusieurs dimensions de plusieurs manières :
- v1 : array [1..10] of array [1..20] of real -> Tableau de 10 éléments,
chaque élément étant un tableau de 20 réels. On accède à l'élément d'indice i dans 1..10 et j
dans 1..20 par v1[i][j].
- v2 : array [1..10, 1..20] of real -> Tableau de 10 * 20 réels.
On accède à l'élément d'indice i dans 1..10 et j dans 1..20 par v2[i,j].
Exemple Mise à 0 du tableau v2 |
VAR v2 : array [1 ..10 , 1 ..20 ] of real ;
i, j : integer ;
BEGIN
for i := 1 to 10 do
for j := 1 to 20 do
v2[i,j] := 0 .0 ;
END .
|
V.2.2. Tableaux de record
On peut créer des tableaux d'enregistrements,
et des enregistrements qui contiennent des tableaux.
PROGRAM Ecole;
CONST MaxEleves = 35 ;
MaxNotes = 10 ;
TYPE note_t = array [1 ..MaxNotes] of real ;
eleve_t = Record
age, nb_notes : integer ;
notes : note_t;
moyenne : real ;
End ;
classe_t = array [1 ..MaxEleves] of eleve_t;
VAR c : classe_t;
nb_eleves, i, j : integer ;
BEGIN
for i := 1 to nb_eleves do
begin
writeln (' Eleve n. ' , i);
writeln (' age : ' , c[i].age);
write (' notes : ' );
for j := 1 to c[i].nb_notes do
write (' ' , c[i].notes[j]);
writeln;
writeln (' moy : ' , c[i].moyenne);
end ;
END .
|
-
On a comme d'habitude le droit de faire une copie globale entres variables du même type :
VAR c1, c2 : classe_t;
e : eleve_t;
i, j : integer ;
BEGIN
c2 := c1;
e := c1[i];
c1[i] := c1[j];
c1[j] := e;
END .
|
- Exemple de passages de paramètres : on écrit une procédure affichant un eleve_t.
PROCEDURE affi_eleve (e : eleve_t);
VAR j : integer ;
BEGIN
writeln (' age : ' , e.age);
write (' notes : ' );
for j := 1 to e.nb_notes do
write (e.notes[j]);
writeln;
writeln(' moy : ' , e.moyenne);
END ;
BEGIN
for i := 1 to nb_eleves do
begin
writeln(' Eleve n. ' , i);
affi_eleve (c[i]);
end ;
END .
|
affi_eleve(e)
ne connait pas le numéro de l'élève ; l'appelant, lui, connait le numéro, et l'affiche avant l'appel.
On peut encore écrire une procédure affi_classe :
PROCEDURE affi_classe (c : classe_t ; nb : integer );
VAR i : integer ;
BEGIN
for i := 1 to nb do
begin
writeln (' Eleve n. ' , i);
affi_eleve (c[i]);
end ;
END ;
BEGIN
affi_classe
(c, nb_eleves);
END .
|
V.3. Le type string
On code une chaîine de caractère telle que 'bonjour' dans un objet de type string.
V.3.1. Principe
Syntaxe :
string [m] où m est une constante entière donnant le nombre maximum de
caractères pouvant être mémorisés. Exemple :
VAR s : string [80 ];
BEGIN
s := ' Le ciel est bleu. ' ;
writeln (s);
END .
|
Codage
Ayant déclaré s : string[80], comment sont codés les caractères ?
En interne, Pascal réserve un array [0..80] of char.
Le premier caractère est s[1], le deuxième est s[2], etc.
La longueur courante de la chaîne est codé dans la case 0 (-> ord(s[0])).
Remarque
- Affecter une chaîne plus longue que l'espace réservé à la déclaration est
une erreur.
- Comme la longueur courante est codée sur un char,
elle est limitée à 255.
V.3.2. Opérateurs sur les strings
a := '' Chaîne vide (longueur 0).
a := b Recopie de b dans a.
a := c + d Concaténation en une seule chaîne. c et d de types string ou char ;
le résultat est un string.
length(a) : Longueur courante de a, résultat entier.
CONST Slogan = ' lire la doc ' ;
VAR s1, s2 : string [100 ];
i : integer ;
BEGIN
s1 := ' veuillez ' ;
s2 := s1 + Slogan;
writeln(' s2 = ' ' ' , s2, ' ' ' ' );
writeln(' Longueur courante de s2 : ' , length(s2) );
write (' Indices des ' ' l ' ' dans s2 : ' );
for i := 1 to length(s2) do
if s2[i] = ' l ' then
write (i, ' ' );
writeln;
END .
|
Comparaison entre 2 string : les opérateurs =, <>, <, >, <
=, >=, sont utilisables, etle résultat est un booléen.
La comparaison se fait selon l'ordre lexicographique du code ASCII.
Exemple : Soit b un booléen ; b est-il vrai ou faux ?
b := ' A la vanille ' < ' Zut ' ;
b := ' bijou ' < ' bidon ' ;
b := ' Bonjour ' = ' bonjour ' ;
b := ' zim boum ' > ' attends ! ' ;
|
Exercice : On considère le type LongString suivant.
CONST longStringMax = 4096 ;
TYPE LongString = record ;
c : array [1 ..LongStringMax] of char ;
l : interer;
end ;
|
Ecrire les procédure et fonctions suivantes :
FUNCTION longueur(s1 : LongString ) : integer ;
FUNCTION est_inferieur (s1, s2 : LongString ) : boolean ;
FUNCTION est_egal (s1, s2 : LongString ) : boolean ;
PROCEDURE concatene (s1, s2 : LongString ; var s3 : LongString);
|
VI. Fichiers séquentiels
Les entrées/sorties dans un ordinateur sont
la communication d'informations entre la mémoire de
l'ordinateur et ses périphériques (disques, clavier, écran, imprimante, etc).
Les entrées/sorties se font par le biais de fichiers séquentiels.
Un fichier séquentiel est une collection de données de même type (souvent de
caractères), dans laquelle les donnèes ne peuvent être lues ou écrites que les unes
après les autres, en commençant par le début et sans retour possible en arrière.
Un fichier peut être vide ; il peut avoir une fin ou non ; il peut être ouvert (accessible) ou fermé ;
une lecture peut être << en attente > >.
VI.1. Le clavier et l'écran
Le clavier et l'écran sont gérés comme des fichiers particuliers :
ce sont des fichiers texte, toujours ouverts et sans fin; ils sont désignés par les variables
prédéfinies input et output (dont on ne se sert quasiment jamais).
VI.1.1. Affichage avec write
La procédure write() permet d'afficher un ou plusieurs paramètres.
writeln() fait la même chose puis rajoute un saut de ligne.
| Trois ecritures equivalentes : |
writeln (a, b, c, d);
write (a, b, c, d); writeln;
write (a); write (b); write (c); write (d); writeln;
|
- Le résultat de l'affichage dépend du type du paramètre :
VAR e : integer ;
c : char ;
b : boolean ;
r : real ;
s : string [32 ];
BEGIN
e := 12 ;
c := ' A ' ;
b := true ;
r := 23 .0 ;
s := ' toto ' ;
writeln (e, ' | ' , c, ' | ' , b, ' | ' , r, ' | ' , s);
END .
|
affiche : 12|A|TRUE|2.300000E+01|toto
Formatter l'impression des variables
- Soit v un entier, un booléen, un caractère ou un string. write(v:8)
dit à write d'afficher v sur au moins 8 caractères. Si le nombre de caractères
(signe éventuel compris) est > 8, v est complètement affiché ;
si il est < 8, des espaces sont rajoutés à gauche pour compléter. .
writeln (e:5 , ' | ' , c:3 , ' | ' , b:5 , ' | ' , s:6 );
|
Ainsi affiche : ___12|___A|_TRUE|__toto
- Soit r un réel.
write(r:10); dit à write d'afficher r en notation scientifique,
sur au moins 10 caractères, signes de la mantisse et de l'exposant compris.
Cette fois c'est d'abord le nombre de chiffres après la virgule qui change
de 1 à 10, puis au besoin des espaces sont ajoutés à gauche.
De plus le dernier chiffre de la mantisse affichée est arrondi.
r := 2 / 3 ;
writeln (r:8 , ' | ' , r:10 , ' | ' , r:18 );
|
affiche : _6.7E-01|_6.667E-01|__6.6666666667E-01
Autre formattage de r réel
- write(r:8:4); dit à write d'afficher r en notation simple, sur au
moins 8 caractères, dont 4 chiffres après la virgule (le dernier étant arrondi).
Ainsi writeln (r:8:4);
affiche : __0.6667
Bilan
- Le formattage permet d'aligner des chiffres.
- Ne pas oublier de mettre des espaces autour des variables pour que le
résultat ne soit pas tout aglutiné et illisible.
- On ne peut afficher que des types simples.
VI.1.2. Lecture avec read
La procédure read() permet de lire un ou plusieurs paramètres.
readln() fait la même chose puis fait un readln;
| Trois écritures équivalentes : |
readln (a, b, c, d);
read (a, b, c, d); readln;
read (a); read (b); read (c); read (d); readln;
|
Remarques
- À l'exécution d'une de ces lignes, on peut rentrer les données en les séparant
par des espaces, des tabulations ou des retours chariot
-
Il faut que les données lues correspondent au type attendu de chaque variable,
sinon il y a une erreur à l'exécution.
Le comportement de read() et readln; étant complexe, regardons plus
en détail se qui se passe à l'exécution.
-
L'utilisateur tape une série de caractères, avec de temps à autres
des retours chariot
-
Pendant la frappe, les caractères sont stockés dans un buffer
(une mémoire tampon) ; à chaque ,
le contenu du buffer est envoyé au programme Pascal (y compris le ).
- De son côté, read(v); lit une donnée dans le buffer, ou attend
le buffer suivant. Le read(v); attend donc quand
- on n'a pas tapé de ,
- ou qu'on a tapé une ligne vide,
- ou que toutes les données dans le buffer ont déjà été lues.
- readln; attend le prochain , puis vide le buffer. Attention les données
non lues dans le buffer sont alors perdues pour de futurs read().
Exemple |
VAR a, b, c, d : integer ;
BEGIN
readln (a, b);
readln (c, d);
writeln (' Lu : ' , a, ' ' , b, ' ' , c, ' ' , d);
END .
|
Essais : |
1
2
3
4
Lu : 1 2 3 4
|
1 2
3 4
Lu : 1 2 3 4 |
1 2 3
4 5 6
3 et 6 perdus |
Le même programme avec read (a, b, c, d); readln;
Produit : |
1
2
3
4
Lu : 1 2 3 4
Idem
|
1 2
3 4
Lu : 1 2 3 4
Idem
|
1 2 3
4 5 6
5 et 6 perdus |
Remarque
Le type des objets lus a une grande importance pour read().
Dans notre programme exemple, voici ce qui se passe si on déclare les 4 variables en char :
VAR a, b, c, d : integer;
1 2 3 4
Lu : 1 2 3 4
|
VAR a, b, c, d : char;
1 2 3 4
Lu : 1 _ 2 _
|
Remarque
On ne peut faire un read() que sur un entier, un réel, un caractère ou une chaîne de caractères ;
on ne peut pas faire un read() sur un booléen.
Algorithme de lecture d'une suite de caractères tapés au clavier, se terminant par un '.'
Option : on affiche le code ASCII de chaque caractère.
CONST CarFin = ' . ' ;
VAR c : char ;
BEGIN
read (c);
while c <> CarFin do
begin
writeln (c, ' ' , ord(c));
read (c);
end ;
readln;
END .
|
Exécution : |
Salut
S 83
a 97
l 108
u 117
t 116
13
10
|
Ciao.bye
C 67
i 105
a 97
o 111
|
VI.2. Fichiers de disque
Les fichiers de disque permettent de stocker des informations de manière
permanente, sur une disquette ou un disque dur.
Ces informations persistent même lorsque l'ordinateur est éteint.
L'inconvénient est que ces données ne sont pas en mémoire vive ; on n'y accède pas
directement, comme c'est le cas avec un vecteur.
En fait on va lire ou écrire des données une à une sur le disque, étant donné
qu'il s'agit de fichiers séquentiels.
VI.2.1. Notions générales
Sur un disque, un fichier a un nom, par exemple 'a :\mass\tp4.pas'
On peut coder ce nom dans un string, par exemple nomf.
Dans un programme qui doit manipuler ce fichier, il faut une variable
pour le désigner, par exemple f.
Déroulement des opérations
- Déclarer la variable f
f : text ; ou f : file of qqchose ;
- Assigner la variable f au fichier de nom nomf
assign (f, nomf) ;
- Ouvrir le fichier f pour pouvoir y lire ou y écrire les données
reset (f) ; ou rewrite (f) ;
- Lire ou écrire des données
read (f, donnee) ; ou write (f, donnee) ;
- Quand on a fini, on ferme le fichier
close (f) ;
Lecture ou écriture
On ouvre un fichier soit en lecture, soit en écriture.
On ne peut pas faire les deux en même temps.
- En lecture : on fait reset(f); puis des read(f, ...);
Si le fichier n'existe pas, il y a une erreur.
- En écriture : on fait rewrite(f); puis des write(f, ...);
Si le fichier n'existe pas, un rewrite le crée. Si il existe déjà, le rewrite l'écrase, c'est-à-dire que l'ancien contenu est définitivement perdu.
Fin du fichier
En lecture, avant de faire un read, il faut tester si il y a encore
quelque chose à lire ; on n'a pas le droit de faire un read si la fin du fichier est atteinte.
La fonction eof(f) retourne true si la fin du fichier est atteinte.
Deux familles de fichiers
On distingue les fichiers de texte des fichiers d'éléments.
VI.2.2. Fichiers de texte
Les fichiers de textes sont les fichiers que vous pouvez éditer,
comme par exemple vos fichiers pascal.
Déclaration |
VAR
f : text;
c : char ;
s : string [255 ];
x : integer ;
r : real ;
|
Lecture
- read (f, c); lit un caractère dans f.
- readln (f, s); lit une ligne complète dans f
(toujours readln sur un string).
- read (f, x); lit un entier dans f. On peut de
la même manière lire un réel.
Écriture
- write (f, c) ; écrit un caractère dans f.
- write (f, s); écrit le string dans f. Pour passer
à la ligne on fait writeln(f);
- read (f, x); lit un entier dans f. On peut de
la même manière lire un réel.
- write (f, x, ' '); écrit un entier dans f. On peut de la même manière écrire
un réel ou un booléen. Il vaut mieux rajouter un espace (ou un retour chariot) après chaque
donnée pour que
lors d'une relecture ultérieure, les données ne soit pas accolées en un bloc illisible.
- write (f, r:8:2); écrit un réel formatté dans f.
Morale
En lecture comme en écriture, la manipulation des fichiers texte se passe très
naturellement, de la même façon que la lecture au clavier ou l'écriture à l'écran.
Tous les algorithmes de lecture vus en TD (Horner, compter les 'LE') sont
directement applicables sur les fichiers texte.
En fait, le clavier et l'écran sont tout simplement considérés comme des
fichiers texte, les fichiers input et output.
VI.2.3. Fichiers d'élements
Les fichiers d'éléments sont des copies de la mémoire vive, les éléments
étant tous du même type.
Le type d'élément peut être un type simple, un enregistrement, un tableau, etc.
Déclaration |
TYPE element_t = record
age : integer ;
majeur : boolean ;
end ;
VAR f : file of element_t;
e : element_t;
|
Lecture
read (f, e); lit un élément dans f. On ne fait jamais de readln.
Écriture
write(f, e); écrit un élément dans f. On ne fait jamais de writeln.
Schémas types
write(f, e); écrit un élément dans f. On ne fait jamais de writeln.
- Mettre un vecteur vec de nb éléments dans un fichier.
VAR
vec : array [1 ..vmax] of element_t;
nb, i : integer ;
BEGIN
assign (f, nomf);
rewrite (f);
for i := 1 to nb do
write (f, vec[i]);
close (f);
END ;
|
- Opération inverse ; on ne connait pas nb au départ.
BEGIN
assign (f, nomf);
reset (f);
nb := 0 ;
while not eof(f) and (nb < vmax) do
begin nb := nb+1 ; read (f, vec[nb]); end ;
close (f);
END ;
|
VI.2.4. Gestion des erreurs
L'ouverture d'un fichier peut provoquer une erreur, qui plante le programme.
Par exemple, si on veut ouvrir un fichier en lecture, ce fichier doit exister.
Si on veut créer un fichier, le chemin du fichier doit être valide.
Chaque compilateur fournit sa propre méthode pour éviter un plantage.
Sous Delphi et sous Turbo Pascal,
on encadre reset ou rewrite entre 2 options de compilations spéciales :
{$I-} désactive temporairement le contrôle des
entrées/sorties
{$I+} le rétablit.
Juste après on regarde si il y a eu une erreur en testant
la variable IoResult.
Exemple En écriture |
BEGIN
assign (f, nomf);
rewrite (f);
ok := IoResult = 0 ;
if not ok
then writeln (' Erreur création fichier ' , nomf)
else begin
...
write (f, ...);
...
close (f);
end ;
END ;
|
Exemple En lecture |
BEGIN
assign (f, nomf);
reset (f);
ok := IoResult = 0 ;
if not ok then
writeln (' Erreur lecture fichier ' , nomf)
else
begin
...
while not eof(f) do
begin
read (f, ...);
...
end ;
...
close (f);
end ;
END ;
|
Remarque
On peut aussi utiliser IoResult dans la lecture au clavier,
en encadrant read entre {$I-} et {$I+}.
Par exemple lorsqu'on attend un réel et qu'une lettre est tapée,
le programme, au lieu de planter,
détectera l'erreur et pourra redemander une frappe.
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 ©
2010 Cours pascal. Aucune reproduction, même partielle, ne peut être
faite de ce site ni 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.