Cours Delphi [PDF]

  • 0 0 0
  • Gefällt Ihnen dieses papier und der download? Sie können Ihre eigene PDF-Datei in wenigen Minuten kostenlos online veröffentlichen! Anmelden
Datei wird geladen, bitte warten...
Zitiervorschau

Chapitre 1 ENVIRONNEMENT DE TRAVAIL

A la fin de cette partie, vous serez capable de: • connaître quelques composants de base de Delphi • utiliser quelques outils, • créer, ouvrir et sauvegarder un projet, • distinguer entre propriétés et événements concernant les composants Delphi

1.1 Introduction Après son lancement Delphi se présente sous la forme de 4 fenêtres. Cette présentation n'est pas courante parmi les applications Windows. Toutefois elle se révèle relativement pratique. La première fenêtre occupe la partie supérieure de l'écran; elle correspond à l'environnement de programmation proprement dit:

Fig. 1.1

Cette fenêtre contient: • la barre de titre • la barre de menu de Delphi • une zone "barre d'outils" (sur la gauche) • une zone contenant les divers composants regroupés par familles.

La seconde fenêtre se trouve par défaut à gauche de l'écran: c'est l'inspecteur d'objets. Il permet de visualiser, pour chaque objet ou composant, les propriétés et les événements auxquels l'objet peut répondre:

fig. 1.2

Delphi (J-C Armici janvier 2003 www.unvrai.com)

page 1-1

La troisième fenêtre constitue la fiche principale de la future application Delphi. Il s'agit, au départ, d'une fenêtre vide dans laquelle on placera les divers objets:

fig. 1.3

La dernière fenêtre, cachée sous la précédente constitue l'"éditeur" proprement dit, contenant le code source de l'application:

fig. 1.4

On peut déjà constater que Delphi génère automatiquement le code correspondant à la fiche principale Form1. Dans Delphi, chaque fiche est décrite comme un objet (associé à une classe). A ce point, une remarque s'impose. Du fait de différentes traductions de l'anglais on peut trouver plusieurs termes décrivant le même objet à savoir une fenêtre: forme, feuille, fiche, fenêtre. Nous utiliserons le terme "fiche". Delphi applique un principe fondamental lors de la création d'applications:

Chaque fiche correspond à une unité

Delphi (J-C Armici janvier 2003 www.unvrai.com)

page 1-2

On peut voir sur la figure 1.4 que Delphi a automatiquement déclaré la variable Form1. Cette variable définit un objet qui est une instance de la classe Tform1. Avant d'aller plus loin, il faut insister sur un point essentiel de l'utilisation de Delphi. Pour le confort de l'utilisateur, Delphi donne automatiquement un nom à chaque nouvel objet ou composant. Il s'agit d'un nom par défaut. Par exemple Form1, Button1, Button2, Listbox1... On peut bien entendu travailler avec ces noms ou donner un nom (explicite) à chaque nouvel objet. Au lieu de Button1 on aura, par exemple, Quitter; au lieu de Listbox1 on aura, par exemple Couleurs.

1.2 Quelques options des menus et quelques outils Dans le menu Outils, l'option Options d'environnement, permet d'activer les options d'autoenregistrement, ce qui force l'enregistrement du projet avant chaque exécution. Lors des premiers pas en Delphi, il est vivement conseillé d'enclencher les options d'auto-enregistrement.

fig 1.5

Sur la fiche (voir figure 1.3) une grille en pointillés est affichée. Dans le concepteur de fiche, on peut choisir d'afficher ou non la grille, d'aligner ou non les objets sur la grille et choisir également la taille des mailles de la grille. Lors du premier démarrage de Delphi, une nouvelle application est crée. Lors des démarrages suivants, la dernière application utilisée dans Delphi est automatiquement rechargée. Pour démarrer une nouvelle application, il faut choisir l'option Nouvelle application du menu Fichier.

Delphi (J-C Armici janvier 2003 www.unvrai.com)

page 1-3

fig 1.6

Pour sauvegarder une application, il faut choisir l'option Tout enregistrer du menu Fichier. Une règle à suivre absolument est de créer un répertoire par application. Delphi créant plusieurs fichiers pour une application donnée, il plus facile de les retrouver s'il ne sont pas enregistrés avec d'autres fichiers de noms pratiquement identiques. Lors du premier "tout enregistrement" de l'application, une fenêtre permet de choisir l'emplacement de sauvegarde et même de le créer. L'icône Tout enregistrer se retrouve dans la barre d'outils.

fig 1.7

Pour exécuter une application, il faut choisir l'option Exécuter du menu Exécuter. Si les options d'auto-enregistrement ont été sélectionnées et que l'application n'a encore jamais été sauvegardée, la fenêtre d'enregistrement s'affiche. L'application est ensuite compilée puis exécutée, si elle ne contient pas d'erreur. L'icône Exécuter se retrouve dans la barre d'outils.

fig 1.8

Quitter L'option Quitter du menu Fichier permet de quitter Delphi, après les vérifications d'usage, à savoir la sauvegarde éventuelle des fichiers.

Delphi (J-C Armici janvier 2003 www.unvrai.com)

page 1-4

1.3 Premier projet Ce premier projet en Delphi a l'allure suivante:

fig 1.9

La fiche contient cinq boutons: •

Un clic sur les boutons Rouge, Bleu ou Jaune change la couleur de la fiche en celle indiquée



Un clic sur le bouton Reset remet la fiche à sa couleur initiale



Un clic sur le bouton Fin permet de quitter l'application.

Nous allons voir, pas à pas, comment réaliser cette application. Le titre qui s'affiche dans la barre de titre de la fiche est "Premier projet". Il faut l'indiquer dans la propriété Caption de la fiche:

fig 1.10

Delphi baptise la première fiche d'un projet du nom de Form1. Pour la sélectionner, il faut cliquer n'importe où sur la fiche. Son nom apparaît alors dans le haut de l'Inspecteur d'objets. Il faut que l'onglet Propriétés soit sélectionné pour pouvoir modifier les propriétés de la fiche. Le texte indiqué pour la propriété Caption apparaît dans la barre de titre de la fiche.

Delphi (J-C Armici janvier 2003 www.unvrai.com)

page 1-5

Une autre propriété nous intéresse pour cette application, c'est la propriété Color. La valeur par défaut choisie par Delphi est "clBtnFace". Un clic sur la flèche fait apparaître une liste de couleurs prédéfinies, qui sont soit des "vraies" couleurs, comme par exemple clBlue, clRed, etc. soit des couleurs définies dans la section Couleur du Panneau de configuration Windows (par exemple, clBtnFace renvoie à la couleur système des faces de boutons).

fig 1.11

Pour l'instant, il ne faut pas modifier cette propriété. Il faut ensuite ajouter 5 boutons sur la fiche. L'onglet Standard de la palette des composants étant sélectionné, cliquez sur l'icône du bouton puis sur la fiche à l'emplacement choisi (coin supérieur gauche). Il faut répéter ces opérations pour les quatre autres boutons. Delphi leur donne les noms Button1,.., Button5 qui sont également affichés sur chacun des boutons.

fig 1.12

Quelques propriétés des boutons: •

Caption: texte affiché sur le bouton. Une esperluette ( & ) précède le raccourci



Name:

nom du bouton, utilisé par le programmeur



Top:

position verticale, nombre de pixels le séparant du haut de la fiche



Left:

position horizontale, nombre de pixels le séparant de la gauche de la fiche



Height:

hauteur du bouton, en pixels



Width:

largeur du bouton, en pixels

Il faut changer le nom (propriété Name) de chaque bouton, ce qui modifie automatiquement la propriété Caption si elle n'a pas déjà été modifiée. Il faut ensuite ajouter l'& dans la propriété Caption, devant la lettre raccourci. L'application n'est pas terminée, mais nous allons déjà l'exécuter pour voir ce que Delphi génère. La touche est le raccourci pour Exécuter. Etant donné que nous avions sélectionné les options d'auto-enregistrement, il faut maintenant indiquer le répertoire où stocker l'application. ATTENTION!!! Une seule application par répertoire !!! Delphi propose le nom Unit1.pas, pour l'unité Pascal attachée à la fiche, et project1.dpr pour le projet. Il est conseillé de choisir un autre nom pour le projet (par exemple: exemple1), car c'est ce nom que portera l'exécutable créé par Delphi.

Delphi (J-C Armici janvier 2003 www.unvrai.com)

page 1-6

Le programme s'exécute. Il est possible de cliquer sur les boutons, mais ça n'a aucun effet, Il est possible de maximiser, minimiser et fermer la fenêtre, et ça a de l'effet… A la fermeture de la fenêtre, nous nous retrouvons dans l'environnement de développement. Il manque à l'application, une action derrière chaque bouton. Je veux que quand l'utilisateur clique sur le bouton Rouge, la fiche devienne rouge. L'événement déclencheur est: l'utilisateur clique sur le bouton Rouge. Ce qui se produit est: la fiche devient rouge. Pour le programmer: •

Sélectionner le bouton Rouge



Choisir l'onglet Evénements dans l'inspecteur d'objets



Double-cliquer à côté de l'événement OnClick, l'éditeur apparaît:

fig 1.13

Le curseur se retrouve dans la procédure Tform1.RougeClick (la procédure qui sera exécutée lorsque le bouton Rouge de la fiche Form1 sera cliqué). Le code à y écrire est: form1.color := clred; la valeur clRed (rouge) est assignée ( := ) à la propriété Color de la fiche Form1. Vous noterez qu'il n'y a pas de distinction majuscule/minuscule au niveau du langage de programmation. Pour les boutons Bleu, Jaune et Reset, la manière de procéder est semblable, la couleur assignée lors du Reset est clBtnFace. Un clic sur le bouton Fin doit provoquer la fermeture de l'application, ce qui correspond au code: Application.terminate; Note: la touche commute entre la fiche et l'éditeur. Voici le code de ce premier exemple en Delphi: procedure TForm1.RougeClick(Sender: TObject); begin form1.Color := clred; end; procedure TForm1.BleuClick(Sender: TObject); begin form1.color := clblue; end; procedure TForm1.JauneClick(Sender: TObject); begin form1.color := clyellow; end; procedure TForm1.ResetClick(Sender: TObject); begin form1.color := clBtnFace; end;

Delphi (J-C Armici janvier 2003 www.unvrai.com)

page 1-7

procedure TForm1.FinClick(Sender: TObject); begin application.Terminate; end;

1.4 Labels et Edits

Label Une étiquette (Label) sert à afficher du texte fixe, c'est-à-dire non modifiable par l'utilisateur. Cependant il arrive assez souvent de modifier un label au moment de l'exécution du programme.

Label Edit fig 1.14

Dans cet exemple (figure 1.14) on peut voir un cas courant: l'association d'un Label et d'un Edit. Mais un Label peut aussi servir à placer des titres sur une fiche. Quelques propriétés des labels: •

Caption: texte constituant le Label



Color:

couleur de fond



Font:

choix du style de l'écriture (fonte, taille, couleur du texte...)



Name:

nom du composant



Visible

indique si le composant est visible ou non lors de l'exécution

Edit Un Edit est un composant utilisé chaque fois que l'utilisateur doit fournir une information sous forme de texte (voir figure 1.14). Généralement il est utilisé lorsque le texte à saisir n'est pas trop long, car un Edit ne peut pas avoir de Scroll bar ni être "multilignes". Pour saisir de longs fragments de texte on utilisera plutôt un composant Memo. Quelques propriétés des Edits: •

BorderStyle:

style de bordure autour de l'Edit.



Color:

couleur de fond.



Enabled: actif (si oui l'utilisateur peut y accéder, sinon il n'est pas possible d'atteindre l'Edit)



Font:

choix du style de l'écriture (fonte, taille, couleur du texte...)

Delphi (J-C Armici janvier 2003 www.unvrai.com)

page 1-8



MaxLength:

limite le nombre de caractère à saisir (0 signifie qu'il n'y a pas de limite)



Name:

nom du composant



TabStop:

si oui, on peut se placer sur le composant en appuyant sur la touche



Text:

texte qui apparaît par défaut dans l'Edit



Visible:

indique si le composant est visible ou non lors de l'exécution

Evénements importants: •

OnChange: lorsque le contenu de l'Edit change. Par exemple quand l'utilisateur tape, supprime, modifie des caractères dans l'Edit.



OnEnter: lorsque le focus arrive sur l'Edit. En d'autres termes, lorsque l'Edit devient actif (le curseur y arrive).



OnExit:



OnKeyPress: lorsque l'utilisateur tape sur une touche dans l'Edit.

lorsque l'Edit perd le focus.

La propriété Text de l'Edit peut contenir uniquement du texte. Pour afficher la valeur d'un nombre, il faut d'abord le convertir en texte ou chaîne de caractères. Si i est un nombre de type entier:

Edit1.Text := IntToStr (i);

Si x est un nombre de type réel:

Edit1.Text := FloatToStr (x);

Ou

Edit1.Text := FloatToStrF (x, ffFixed, 10, 4);

Lorsque l'utilisateur saisit un nombre dans un Edit, Editx.Text contient une chaîne de caractères. Pour pouvoir effectuer des calculs, il faut convertir la chaîne de caractères. Pour obtenir une valeur entière, à condition que la chaîne de caractères à convertir ne contienne pas de caractères illicites: i := StrToInt (Edit1.Text); Pour obtenir une valeur réelle, à condition que la chaîne de caractères à convertir ne contienne pas de caractères illicites: x := StrToFloat (Edit1.Text);

Delphi (J-C Armici janvier 2003 www.unvrai.com)

page 1-9

Exercice 1.1: Reprendre les mêmes objets que dans l'exemple de la page 1.7 et y ajouter un Edit qui est de même couleur que la fiche et ne contient pas de texte. Un clic sur un bouton doit provoquer le changement de couleur de l'Edit et non plus de la fiche. La propriété Text de l'Edit contient le texte affiché dans l'Edit. Voici comment se présente le programme:

Exercice 1.2: Placer quatre boutons (Matin, Midi, Soir, Nuit) et un Edit sous chacun d'eux. Au départ, seuls les boutons sont visibles. Un clic sur le bouton Matin fait apparaître l'Edit situé en-dessous et où il est écrit Matin, si un autre Edit est visible, il doit disparaître. Idem pour les trois autres boutons. Pour rendre l'Edit1 visible: Edit1.Visible := true; Pour rendre l'Edit1 invisible: Edit1.Visible := false; Pour mettre un texte par programmation dans l'Edit1: Edit1.Text := 'Bonjour'; Voici comment se présente le programme:

Delphi (J-C Armici janvier 2003 www.unvrai.com)

page 1-10

Delphi (J-C Armici janvier 2003 www.unvrai.com)

page 1-11

Chapitre 2 LE LANGAGE PASCAL DE BASE

A la fin de cette partie, vous serez capable de: • connaître la structure du langage Pascal (non objet); • utiliser les divers types de données du Pascal; • utiliser des instruction séquentielles, itératives et sélectives

2.1 La programmation structurée Un ordinateur peut être assimilé à un système produisant des résultats à partir d'informations fournies et de "marches à suivre" permettant de les traiter. Les informations sont constituées par des données, et les méthodes de traitement par des algorithmes. Pour obtenir des résultats, la description des données et les algorithmes doivent être codés sous forme de programmes interprétables par l'ordinateur. En effet, le processeur de celui-ci ne peut exécuter qu'un nombre relativement restreint d'instructions élémentaires (le code machine). Les programmes sont donc le résultat d'une succession d'étapes comprises entre la spécification informelle du problème et sa codification. Il y a ainsi entre ces deux pôles un "trou" qu'il s'agit de combler. Parmi les moyens ou les outils permettant d'y parvenir on peut citer notamment des environnements de production de logiciel (par exemple Delphi), des méthodes fournissant un encadrement au concepteur ou encore des langages de spécification permettant de préciser les étapes intermédiaires. Un autre aspect du rapprochement de la phase de codification vers la spécification du problème est constitué par le développement ou l'utilisation de langages de programmation permettant un niveau d'abstraction plus élevé. Un des objectifs de la programmation structurée est la conception de logiciel fiable, efficace et d'une maintenance plus aisée. Il peut être atteint de manière asymptotique et par divers moyens. Les trois caractéristiques citées peuvent difficilement être évaluées dans l'absolu, car elles dépendent souvent de critères relatifs et subjectifs. Un programme n'est pas juste ou faux; sa qualité est une notion globale, constituée par plusieurs éléments, dont nous allons étudier les plus importants. La fiabilité est une propriété informelle et parfois difficile à cerner. Cette propriété peut être atteinte grâce à deux qualités du langage de programmation. D'abord, la facilité d'écriture doit permettre d'exprimer un programme de façon naturelle ou en termes du problème à résoudre. Le programmeur ne doit pas être dérangé par des détails ou des habitudes du langage, mais doit pouvoir se concentrer sur la solution recherchée. Les langages modernes de haut niveau tendent vers cet objectif. Ensuite, la lisibilité du programme doit permettre d'en saisir aisément la construction logique et de détecter plus facilement la présence d'erreurs. Dans cette optique, l'instruction goto, par exemple, rend difficile la lecture du programme de façon descendante. Toutefois, dans certains cas, les objectifs énoncés au début de cette section peuvent être atteints dans de meilleures conditions par l'utilisation d'un goto (bien placé et bien documenté) plutôt que par une construction structurée; sa présence est alors acceptable. De telles situations sont toutefois extrêmement rares. Si l'efficacité était au début l'objectif principal de la conception d'un programme, actuellement cette notion a évolué pour englober non seulement des critères de vitesse d'exécution et de place mémoire, mais aussi l'effort requis pour la maintenance du logiciel. En effet, la nécessité de la maintenance impose au logiciel qu'il soit lisible et modifiable. Ces qualités sont souvent liées à des critères esthétiques. On peut néanmoins citer quelques facteurs qui facilitent les interventions dans un programme: un découpage approprié, une mise en page

Delphi (J-C Armici janvier 2003 www.unvrai.com)

page 2-1

claire, un choix adéquat des noms d'objets utilisés, des commentaires cohérents placés judicieusement.

2.2 Forme générale d'un programme Delphi Dans Delphi on est peu confronté au programme proprement dit, mais plutôt à des unités, ce qui constitue une grande différence par rapport au Pascal traditionnel. De plus, le programme (principal), appelé projet: •

est généralement sauvegardé dans un fichier .dpr (pour Delphi Project);



est toujours de petite taille;



est automatiquement créé par Delphi;



contient les références aux unités qui constituent l'application;



initialise l'application, crée les différentes fiches et lance l'exécution de l'application.

En voici un exemple program Project2; uses Forms, Unit1 in 'Unit1.pas' {Form1}, Unit2 in 'Unit2.pas' {Form2}; {$R *.RES} begin Application.Initialize; Application.CreateForm(TForm1, Form1); Application.CreateForm(TForm2, Form2); Application.Run; end. Ainsi, la ligne marquée d'une flèche signifie que le programme utilise l'unité Unit1 stockée dans le fichier Unit1.pas et concerne la fiche Form1. Lors du développement on est sans cesse en train de travailler dans des unités de Delphi. En voici un exemple: unit Unit1; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;

Partie interface

Const MAX = 10; type TForm1 = class(TForm) Button1: TButton; procedure Button1Click(Sender: TObject); private { Déclarations privées } public { Déclarations publiques } end; var Form1: TForm1; tab : array[1..MAX] of integer; procedure Titre (taille: integer);

Delphi (J-C Armici janvier 2003 www.unvrai.com)

page 2-2

implementation uses Unit2; Partie implémentation

{$R *.DFM} procedure TForm1.Button1Click(Sender: TObject); begin form2.show; end; end. Une unité comporte: •

un en-tête



une partie interface dans laquelle figurent les objets exportables (visibles de l'extérieur de l'unité): •

déclaration de constantes, de types (par exemple les objets) et de variables



des déclarations de procédures et/ou fonctions exportables



une partie implémentation dans laquelle figurent les objets privés de l'unité, ainsi que le code des procédures et/ou fonctions déclarées dans la partie interface ainsi que de celles strictement privées;



le corps de l'unité, ou partie d'initialisation, qui est souvent vide et réduite à "end.".

En Pascal, il n'est pas possible de définir ou déclarer un objet à n'importe quel emplacement du programme. Ces définitions doivent être regroupées dans la partie réservée aux déclarations. De plus: Tout objet référencé dans un programme doit avoir été préalablement défini. Cette règle traduit une logique qui veut que des objets nouveaux soient construits uniquement à l'aide d'objets connus. Enfin, on peut signaler une particularité intéressante: des déclarations de constantes, types et variables peuvent être placées entre les procédures. Dans ce cas, le domaine de visibilité de ces objets est constitué par la portion du programme située après ces déclarations. Cette possibilité permettant, par exemple, de disposer de variables "semi-globales" tend à rendre les programmes moins lisibles et va à l'encontre des principes de base liés à la notion de procédure.

2.3 Différents objets d'un programme Si l'on répertorie les différents objets d'un programme, on obtient les catégories suivantes:

identificateurs Chaque fois que l'on fait référence à un objet du programme (une variable, une constante, le nom d'une procédure...), c'est par l'intermédiaire d'un nom, appelé identificateur. Un identificateur est une suite de caractères de longueur non limitée dont le premier doit obligatoirement être une lettre (non accentuée). Après cette première lettre peuvent figurer exclusivement des chiffres, des lettres (non accentuées) ou des caractères de soulignement (dans un ordre quelconque). Le caractère de soulignement est souvent utilisé dans le but d'améliorer la lisibilité. Voici quelques exemples d'identificateurs: A

Epsilon45

revenu_brut

Delphi (J-C Armici janvier 2003 www.unvrai.com)

page 2-3

Le choix des identificateurs peut également favoriser la lecture et la compréhension d'un programme. Il est déconseillé d'utiliser des identificateurs trop courts, ne suggérant aucune signification (par exemple A, X2, z), ainsi que des identificateurs trop longs, nécessitant plus de travail lors de l'écriture, et surtout n'offrant pas forcément une meilleure lisibilité. Delphi ne distingue pas les minuscules des majuscules formant un identificateur. Ainsi les trois noms qui suivent désignent le même objet: Rendement

rendement

RENDEMENT

mots réservés du langage Il s'agit de mots ou de symboles qu'il n'est pas possible d'utiliser comme identificateurs déclarés dans le programme. En voici des exemples: begin

program

and

until

constantes Il s'agit, comme leur nom l'indique, d'objets qui gardent leur valeur tout au long de l'exécution d'un programme. Les constantes peuvent être de différents types, et constituées, entre autres, de nombres, de chaînes de caractères ou de caractères. Voici quelques exemples de constantes: 128

15.625

'A'

'Début'

identificateurs standard Ce sont des identificateurs connus du langage Pascal, mais qui peuvent être redéfinis par l'utilisateur. On trouve parmi les identificateurs standard: •

les types standard exemple:



integer

byte

les procédures standard exemple:



real

reset

les fonctions standard exemple:

sin

ord

chr

round

eoln

2.4 Constantes et variables Le langage Pascal fait une distinction entre constantes et variables (ce qui n'est pas le cas pour des langages comme le BASIC). On utilise une constante chaque fois qu'un objet garde la même valeur tout au long d'un programme. Une constante reçoit une valeur au moment de la compilation du programme et cette valeur ne peut pas être modifiée. Une variable sera au contraire utilisée comme un objet dont la valeur peut être modifiée durant l'exécution du programme. Comme tous les objets définis par le programmeur, les constantes et les variables doivent être déclarées avant leur utilisation. Voici un exemple de déclaration de constantes et de variables: procedure premier;

const

nul code zede var age salaire sexe ...

= = = : : :

0; 'secret'; 'z'; integer; real; char;

Dans le cas des constantes, l'identificateur est suivi du signe "=" et de la valeur que l'on associe à la constante. Pour les variables, l'identificateur est suivi du signe ":" et du type de la variable. Delphi (J-C Armici janvier 2003 www.unvrai.com)

page 2-4

Chaque déclaration de constante ou de variable se termine par un point virgule. Si plusieurs variables sont du même type, il est possible de les déclarer sous forme de liste d'identificateurs (séparés par une virgule) suivie du signe ":" et du type des variables: var

age, enfants, voitures : integer; salaire, taille : real;

Les déclarations précédentes peuvent également s'écrire: var

age enfants voitures salaire taille

: : : : :

integer; integer; integer; real; real;

{ { { { {

du père à charge disponibles maximum du capitaine

} } } } }

2.5 Commentaires Dans le but d'améliorer la lisibilité et la compréhension des programmes, il est fortement conseillé d'introduire des commentaires. Un commentaire est un texte explicatif plus ou moins long, placé dans le programme, et ignoré par le compilateur. Les commentaires sont donc totalement invisibles et inutiles dans la phase de compilation et d'exécution d'un programme, mais d'une importance primordiale dans les phases de conception, de mise au point et de maintenance. Ces explications, visibles uniquement dans les fichiers source (contenant le texte du programme), sont essentiellement destinées aux personnes susceptibles d'analyser un programme ou de lui apporter des modifications. Dans la plupart des cas, les commentaires sont destinés à l'auteur du programme. Mais dans tous les cas où un programme est réalisé en équipe, ou repris par d'autres personnes que l'auteur, les commentaires doivent être une aide à la compréhension, surtout dans certains passages peu évidents. Il n'est pas rare de rencontrer des programmes où les commentaires occupent plus de place que les instructions elles-mêmes. Il ne faut cependant pas oublier de mettre à jour les commentaires lors de la modification de tout ou partie du programme. Un commentaire devenu caduque ou qui n'a pas été mis à jour perd son utilité et peut même devenir un obstacle à la compréhension d'un programme. En Pascal, les commentaires sont reconnus comme tels par le compilateur grâce à des marques de début et de fin qui sont soit des accolades { }, soit les symboles (* et *). Il est possible d'imbriquer un type de commentaire dans l'autre type de commentaire. En voici quelques exemples: (* Programme écrit par :

Durant Eva

*)

{ imbrication de commentaires (* très drôles *)}

Delphi ajoute également un troisième type de commentaire issu du langage C: chaque ligne commençant par // est considérée comme un commentaire. Dans ce cas, le commentaire ne peut s'étendre que sur une ligne et ne possède qu'une marque de début: //ceci est un commentaire du troisième type

2.6 Affectation L'affectation (ou assignation) est l'une des instructions les plus importantes en Pascal. Elle permet de placer une valeur, qui est le résultat de l'évaluation d'une expression, dans une position mémoire référencée par une variable: variable := expression; où variable est l'identificateur d'une variable qui a été déclarée auparavant.

Delphi (J-C Armici janvier 2003 www.unvrai.com)

page 2-5

L'expression peut être une simple variable ou constante, ou bien une combinaison de constantes, variables et opérateurs arithmétiques. Le symbole := indique l'affectation et pourrait être traduit par "prend la valeur de". Les deux signes qui le composent doivent être accolés. En Pascal, on a voulu éviter toute confusion entre le symbole de l'affectation et celui de l'égalité (représenté par le signe "="). Lors d'une affectation, l'expression qui se trouve à droite du symbole := est évaluée, ensuite seulement le résultat de l'évaluation est affecté à la variable située à gauche du symbole :=. De plus, il est impératif que le type de l'expression soit le même que le type de la variable à laquelle on affecte le résultat de l'expression.

2.7 Instructions et blocs d'instructions Nous avons vu précédemment que les instructions sont séparées par des points virgules. Lorsque plusieurs instructions forment logiquement un tout, on est souvent amené à les grouper. On obtient alors un bloc d'instructions. Le début d'un tel bloc est indiqué par le mot réservé begin, alors que sa fin est indiquée par le mot réservé end. On parle parfois d'"instruction composée" au lieu de "bloc d'instructions". Ceci exprime bien le fait qu'un groupement de plusieurs instructions peut être vu comme une seule instruction (indivisible). Le corps d'un programme Pascal est luimême un bloc d'instructions. Voici un exemple de bloc d'instructions: begin age := 55; no := age * 10; end; Lorsque l'on écrit ses premiers programmes, les points virgules posent parfois des problèmes. En fait, ce qui peut sembler au début une contrainte, devient naturel après un temps d'adaptation. La règle concernant les points virgules est la suivante:

Chaque instruction doit se terminer par un point-virgule. Toutefois, le point-virgule peut être omis s'il est suivi des mots réservés end ou until. Il doit être omis s'il est suivi du mot réservé else (sauf s'il s'agit d'une structure sélective case...of). En suivant cette règle, l'exemple précédent aurait pu s'écrire de la manière suivante begin age := 55; no := age * 10 end; Bien que cette forme d'écriture soit tout à fait correcte, il est conseillé de l'éviter au profit de la première citée. L'économie de points virgules peut parfois conduire à des erreurs de syntaxe lors de la modification d'un programme. Ajoutons, par exemple, une ligne à la fin du bloc d'instructions (avant le end). Le risque d'oublier de placer un point virgule à la fin de la ligne qui précède donne le résultat suivant: begin age := 55; no := age * 10 no := no - age end;

(* il manque un ; ici *) (* ligne ajoutée *)

Ce fragment de programme n'est pas correct, car il manque le séparateur entre les deux dernières instructions. Le lecteur jugera lui-même, expérience faite, de l'opportunité de placer un séparateur d'instructions avant un end ou un until.

Delphi (J-C Armici janvier 2003 www.unvrai.com)

page 2-6

2.8 Types scalaires et expressions Parmi les avantages qu'offre le langage Pascal, on trouve une grande richesse de types de données. Nous avons vu précédemment qu'à chaque variable correspond un type. La notion de type est très importante puisqu'elle détermine la nature et l'ensemble des valeurs que peut prendre une variable. Dans cette section nous nous intéressons aux types scalaires, caractérisés par un ensemble de valeurs ordonnées et non structurées. Parmi les types scalaires on trouve les types entiers, réels, booléen et caractère, ainsi que les types énumérés ou définis par l'utilisateur. Nous étudierons également les expressions qu'il est possible de construire sur la base de chacun de ces types. Une expression est une combinaison d'objets qui sont des opérateurs, des opérandes et des parenthèses. Nous serons amenés à évoquer la représentation interne des nombres, ou du moins la place mémoire occupée par une variable d'un type donné. En informatique, l'unité de mesure de la capacité mémoire est l'octet (byte); un octet étant lui-même composé de 8 chiffres binaires (bits) pouvant prendre chacun la valeur 0 ou 1.

Les types entiers Il existe plusieurs type permettant de stocker des valeurs entière. Voici leurs caractéristiques:

Type

Intervalle

Format/taille

Byte

0..255

non signé, 1 octet

Word

0..65535

non signé, 2 octets

Shortint

-128..127

signé, 1 octet

Smallint

-32768..32767

signé, 2 octets

Integer

-2147483648.. 2147483647

signé, 4 octets

Cardinal

0.. 4294967295

non signé, 4 octets

Longint

-2147483648.. 2147483647

signé, 4 octets

0..4294967295

non signé, 4 octets

2^63..2^63–1

Signé, 8 octets

Longword Int64

Tableau 2.1 Il convient de signaler que les types integer et cardinal sont stockés sur 2 octets sous Windows 3.x et sur 4 octets sous Windows 95 ou NT. Malgré leurs particularités, les différents types entiers se comportent de la même manière. Pour garder une cohérence dans les types de données, une opération entre deux nombres entiers doit fournir un résultat de type entier. Ceci est effectivement le cas pour: •

l'addition

4+5

donne 9



la soustraction

12 - 20

donne -8



la multiplication

6*7

donne 42

Mais la division pose un problème: le quotient de 6 par 4 donne 1.5 qui n'est pas un nombre entier. Il a donc fallu implémenter une division, spécifique aux nombres entiers, qui conserve le type des opérandes. En Pascal, cette division entière s'écrit div : 6 div 4

donne 1

10 div 3

donne 3

Delphi (J-C Armici janvier 2003 www.unvrai.com)

page 2-7

Le résultat de cette division correspond à la troncation de la partie décimale. Parallèlement à cette division entière, il est souvent utile de connaître le reste de la division entre deux nombres entiers. L'opérateur mod (abréviation de modulo) permet le calcul de ce résultat: 10 mod 3

donne 1

23 mod 4

donne 3

Lorsque des opérateurs et des opérandes sont combinés de manière à former une expression, il est nécessaire d'avoir une convention permettant de l'évaluer. Ainsi l'expression 4 * 2 + 3 donnera-t-elle le résultat 11 ou bien 20 ? Tout dépend de l'ordre dans lequel sont effectuées les opérations. Les conventions d'évaluation en vigueur en Pascal correspondent à des règles de priorité, semblables à celles qui existent en mathématique: •

les opérateurs div, mod, et * sont prioritaires par rapport aux opérateurs + et - ;



dans chacune de ces deux catégories les opérateurs ont la même priorité;



en cas d'égalité de priorité, les opérations concernées sont effectuées de gauche à droite.

Ainsi: 4*2+3

donne 11

8 + 4 * 3 div 2

donne 14

6 mod 4 * 2 div 3

donne 1

Pour modifier ces règles de priorité, il est toujours possible d'utiliser les parenthèses: 4 * (2 + 3)

donne 20

7 div ((5 mod 3) mod 4)

donne 3

Il est également possible de faire précéder un nombre par l'opérateur unaire "-": -4 * 12

donne -48

4 * (-5)

donne -20

Dans les programmes écrits en TURBO Pascal, les nombres entiers peuvent également être exprimés en notation hexadécimale, autrement dit en base 16. Cette possibilité est appréciée par certains programmeurs, car elle leur permet, dans des situations bien particulières, une représentation des nombres plus proche de celle rencontrée en langage machine. Chaque fois qu'une constante numérique entière intervient dans une expression, il est possible de l'exprimer sous forme hexadécimale. Pour cela il faut placer le signe $ immédiatement devant le nombre exprimé en base 16. Ainsi l'expression total := 2 * 30; peut s'écrire total := $2 * $1E; Cette notation est utilisable pour tous les types entiers.

Les types réels Les différents types réels se différencient par leur domaine de définition, le nombre de chiffres significatifs (précision) et la place mémoire occupée. Le tableau suivant résume ces différentes caractéristiques:

Delphi (J-C Armici janvier 2003 www.unvrai.com)

page 2-8

Type

Intervalle

Chiffres

Taille en octets

Real48

2.9 10-39..1.7 1038

11-12

6

Single

1.5 10-45..3.4 10 38

7-8

4

5.0 10-324..1.7 10308

15-16

8

1.9 10-4951..1.1 104932

19-20

10

-2 +1..2 -1

19-20

8

-922337203685477.5808.. 922337203685477.5807

19-20

8

Double Real Extended

63

Comp Currency

63

Tableau 2.2

Conversion réels/entiers et entiers/réels Il existe deux manières de convertir une valeur réelle en valeur entière: l'arrondi et la troncation. Le Pascal dispose de deux fonctions standard qui effectuent ces opérations sur les nombres réels.

La troncation La fonction trunc agit de manière à tronquer la partie décimale d'un nombre réel. Son utilisation est illustrée par les exemples qui suivent: trunc (4.2)

donne 4

trunc (4.99)

donne 4

trunc (-12.6)

donne -12

trunc (3.01)

donne 3

r := 5.67; i := trunc (r);

{ r est de type réel } { i est de type entier et contient 5}

L'arrondi La fonction round permet d'arrondir un nombre réel à l'entier le plus proche: round (2.99)

donne 3

round (2.5)

donne 3

round (2.499)

donne 2

round (-7.8)

donne -8

r := 4.77; i := round (r);

{ r est de type réel } { i est de type entier et contient 5}

Cette fonction possède également la propriété que round(-x) est équivalent à -round(x). Les fonctions prédéfinies s'expriment par un identificateur (le nom de la fonction) suivi d'un ou plusieurs arguments placés entre parenthèses. L'argument est la valeur transmise à la fonction en vue d'un traitement. Le résultat en retour est porté par l'identificateur de fonction et peut, par exemple, être affiché ou intervenir dans une expression. Edit1.Text := IntToStr (trunc (3.6 * (round (1.4) - 2.5)));

Delphi (J-C Armici janvier 2003 www.unvrai.com)

page 2-9

La conversion d'une valeur entière en une valeur réelle s'effectue sans passer par des fonctions spécifiques. Lors de l'affectation d'une expression de type entier à une variable de type réel, la conversion de type est implicite. Dans les instructions qui suivent, a et b sont des variables de type entier et c de type réel: a := 6; b := 2; c := 2 * a + b;

{c contiendra la valeur 14.0}

Très souvent le programmeur est confronté des expressions mixtes, contenant aussi bien des valeurs entières que réelles. Dans ce cas, le résultat sera de type réel. c := 15 div a;

c contiendra 2.0

15 divisé par 6 donne 2 (division entière); c := 15 / a;

c contiendra 2.5

la division "réelle" de deux entiers donne un résultat réel; c := 1.5 * (a + b);

c contiendra 12.0

il s'agit d'une expression mixte dont le résultat est réel.

Type booléen (boolean) L'ensemble des valeurs constituant le type booléen (boolean) est réduit à deux identificateurs de constantes prédéfinies: true (vrai) et false (faux). On parle souvent de variables ou d'expressions logiques, car le langage Pascal est fortement inspiré de la logique mathématique en ce qui concerne les opérations associées au type booléen. Les constantes et variables de ce type se déclarent de la manière suivante: const demo = true; var

majuscules, termine : boolean;

La constante demo a la valeur true; les variables majuscules et termine peuvent recevoir les valeurs true ou false, selon leur utilisation dans le programme. Parmi les opérateurs booléens on trouve, dans une première catégorie, les opérateurs relationnels. La liste qui suit présente les différents opérateurs relationnels utilisables en Pascal, avec leur symbole et leur signification:


plus grand

=

égal

différent

=

supérieur ou égal

Ces opérateurs se rencontrent dans les expressions booléennes correspondant à des conditions. Comme nous le verrons plus loin, les conditions interviennent notamment dans les instructions sélectives et répétitives. Voici un exemple illustrant l'utilisation d'opérateurs relationnels: begin ... vieux := age >= 60; ... end. Dans ce programme, age >= 60 est une expression booléenne. Selon le contenu de la variable age, le résultat de cette expression est true ou false. La variable vieux étant de type booléen, elle peut recevoir ce résultat. Delphi (J-C Armici janvier 2003 www.unvrai.com)

page 2-10

La seconde catégorie d'opérateurs booléens est constituée par des opérateurs purement logiques. Ils sont au nombre de quatre, et sont illustrés ci-dessous par des exemples: not

négation logique (non)

si x > 12 est vrai, alors not (x > 12) est faux and

conjonction logique (et)

(x > 2) and (x < 10) est vrai, si (x > 2) est vrai et (x < 10) est vrai or

disjonction logique (ou)

(x < 2) or (x > 10) est vrai, si (x < 2) est vrai ou (x > 10) est vrai xor

disjonction logique exclusive

a xor b est vrai si a et b n'ont pas la même valeur logique Comme il est possible, et même très courant, de construire des expressions logiques contenant aussi bien des opérateurs arithmétiques que logiques, il convient d'étendre les conventions de priorité établies précédemment. Nous avons quatre classes d'opérateurs indiquées dans la liste qui suit, en partant de la plus prioritaire: 1) not 2) *

/

div

mod

3) +

-

or

xor

4)


Dans chacune des classes, les opérateurs ont la même priorité. En cas d'égalité de priorité, les opérations correspondantes sont effectuées de gauche à droite. Les parenthèses peuvent servir à forcer la priorité. Voici, à titre d'exemple, comment s'exprimerait en Pascal l'expression "somme est comprise entre 10 et 35": (somme > 10) and (somme < 35) Examinons deux autres exemples d'expressions booléennes: age < date + 100 il s'agit ici de comparer le contenu de la variable age avec le résultat de date + 100. L'opérateur + a une plus grande priorité que l'opérateur 6000 cette expression provoquera un message d'erreur de la part du compilateur, car elle présente un conflit de types. En effet, la priorité de l'opérateur and étant plus élevée que celle des opérateurs relationnels < et >, elle sera interprétée comme age < (40 and revenu) > 6000 qui n'est pas une expression correcte. En Pascal, l'affectation d'une expression booléenne à une variable de type booléen permet souvent d'économiser une instruction sélective if. Le programme qui suit est tout à fait correct: var riche : boolean; revenu : real; ... revenu := StrToFloat (Edit1.Text); if revenu > 4000.0 then

Delphi (J-C Armici janvier 2003 www.unvrai.com)

page 2-11

riche := true else riche := false; ... mais il est plus concis d'écrire: var riche : boolean; revenu : real; ... revenu := StrToFloat (Edit1.Text); riche := revenu > 4000.0; ... Ces deux programmes sont équivalents et riche vaut true ou false selon la valeur introduite par l'utilisateur. Malheureusement la seconde forme est fréquemment négligée au profit de la première.

Type caractère (char) Ce type dénote un ensemble de caractères, fini et ordonné. Chacun de ces caractères peut être exprimé grâce à son code ASCII. Une variable de type caractère (char) peut contenir un seul caractère, généralement spécifié entre apostrophes. Comme nous le verrons plus loin, il est possible de constituer des suites de caractères appelées chaînes de caractères. Voici un petit programme qui met en évidence l'emploi des variables et constantes de type caractère: const effe = 'F'; var lettre : char; bip : char; ... bip := chr(7); lettre := 'h'; mot.text := effe; ... Dans cet exemple, la constante effe contient une lettre majuscule indiquée entre apostrophes et la variable bip contient le caractère dont le code ASCII est 7. Ce code correspond à l'émission d'un bref signal sonore par le haut-parleur de l'ordinateur. La fonction prédéfinie chr permet de référencer tous les caractères, y compris certains caractères du code ASCII qui ne sont pas affichables. L'argument de cette fonction est précisément le code ASCII du caractère désiré. Pour connaître le code des caractères disponibles, il convient de se reporter au manuel de référence de l'ordinateur utilisé. Delphi dispose de deux facilités supplémentaires pour désigner les caractères. L'une est d'indiquer le symbole # suivi du code du caractère. Cette notation est équivalente à la fonction chr et permet, par exemple, d'incorporer des caractères particuliers dans une chaîne de caractères. L'autre concerne uniquement les caractères de contrôle (non affichables). On peut les spécifier en les faisant précéder du symbole '^'. #27'G'

cette suite de caractères comprend le caractère correspondant à la touche , suivi du caractère G. Cette séquence de caractères pourrait, si elle était par exemple envoyée à une imprimante, servir à placer celle-ci dans un mode d'impression donné; if c = #13 then...

cette instruction permet de déterminer si la variable c (de type caractère) contient le caractère correspondant à la touche . On aurait également pu écrire: if c = chr(13) then...

Delphi (J-C Armici janvier 2003 www.unvrai.com)

page 2-12

Type énuméré En Pascal, l'utilisateur peut définir de nouveaux types de données, qui n'existent pas intrinsèquement dans le langage. On parle de types énumérés, ou types scalaires définis par l'utilisateur, ou encore types scalaires déclarés. Le programmeur spécifie lui-même l'ensemble des valeurs appartenant au type qu'il définit. Ces valeurs constituent une liste d'identificateurs de constantes que le programmeur doit indiquer dans la partie du programme réservée aux déclarations. Lors de la déclaration d'un type énuméré on indique, entre parenthèses, toutes les valeurs possibles constituant ce type. Voici comment définir un type énuméré dont les valeurs sont les quatre saisons: type saisons = (printemps, ete, automne, hiver); var periode : saisons; Le type saisons est déclaré à l'aide du mot réservé type. Dès ce moment, il peut figurer dans des déclarations ultérieures, au même titre qu'un type prédéfini. La possibilité de définir ses propres types de données et de les adjoindre aux types prédéfinis permet une plus grande souplesse du langage et du traitement des données. En voici un exemple: ... type fruits = (pommes, poires, ananas, peches, amandes, noix); jours = (lundi, mardi, mercredi, jeudi, vendredi, samedi, dimanche); langages= (VisualBasic, Delphi, Pascal, inconnu,Francais); var dessert, fruit : fruits; aujourdhui : jours; langue : langages; ... dessert := amandes; if aujourdhui = dimanche then liste.items.add ('Congé'); ... fig. 2.3

Les types énumérés sont toutefois soumis à un certain nombre de restrictions. Par exemple, la lecture et l'affichage des valeurs figurant dans un type énuméré ne sont pas autorisés, car il ne s'agit pas de chaînes de caractères, mais d'identificateurs. Une autre contrainte est qu'une même valeur ne peut pas figurer dans deux listes différentes, c'est-à-dire dans deux types énumérés différents. Ainsi, la déclaration suivante n'est pas autorisée: type appareils

= (television, aspirateur, transistor, ventilateur); electronique = (resistance, condensateur, diode, transistor);

En revanche, les possibilités offertes sont intéressantes. Comme pour tous les types scalaires (sauf le type réel), les valeurs des types énumérés sont ordonnées et dénombrables, ce qui rend possible l'utilisation des fonctions prédéfinies pred (prédécesseur), succ (successeur) et ord (numéro d'ordre). Les valeurs d'un type énuméré sont ordonnées en fonction de l'ordre dans lequel elles apparaissent lors de la déclaration, la première valeur d'un type porte le numéro d'ordre 0. Voici quelques exemples, se référant aux déclarations de la figure 2.3, qui illustrent l'emploi de ces nouvelles fonctions en relation avec les types entiers, booléen, caractère et énuméré: ord (6)

vaut 6

ord ('A')

vaut 65

succ ('b')

vaut 'c'

pred (167)

vaut 166

Delphi (J-C Armici janvier 2003 www.unvrai.com)

page 2-13

ord (mardi)

vaut 1

pred (dimanche)

vaut samedi

succ (lundi)

vaut mardi

mais aussi: ord (ord (mercredi)) pred (pommes)

vaut 2 n'est pas correct

succ (Francais)

n'est pas correct

pred (succ(poires))

vaut poires

succ (pred(poires))

vaut poires

pred (pred(dimanche))

vaut vendredi

pred (true)

vaut false

succ (false)

vaut true

ord (false)

vaut 0

Lorsqu'un type énuméré compte 256 valeurs ou moins, ces dernières sont représentées par un nombre de type byte. Dans le cas contraire, elles le sont par un nombre de type word. La fonction pred appliquée au premier élément d'un type ordinal ou la fonction succ appliquée au dernier élément d'un type ordinal ne fournissent pas un résultat correct. Du fait qu'elles sont ordonnées, les valeurs d'un type énuméré peuvent être comparées à l'aide des opérateurs relationnels. Les instructions sélectives et répétitives suivantes tirent profit de cette possibilité: if dessert >= amandes then liste.items.add ('Fruits secs'); if aujourdhui < samedi then liste.items.add ('Jour ouvrable'); while (langue = Pascal) or (langue = Francais) do ... for fruit:=pommes to peches do if dessert = fruit then liste.items.add ('Ce n''est pas un fruit sec'); repeat fruit := succ (fruit); until fruit = noix;

Type intervalle Nous avons vu qu'à chaque type de données est associé un ensemble de valeurs. Les variables déclarées avec un type donné peuvent prendre uniquement des valeurs correspondant à ce type. Mais il est fréquent d'utiliser seulement une portion de l'ensemble des valeurs possibles. Dans ce cas, on pourrait restreindre cet ensemble de valeurs à l'intervalle qui nous intéresse. Le langage Pascal permet cette restriction en faisant appel au "type" intervalle, qui n'est pas un type au sens strict, mais un sous-ensemble de valeurs prises dans un type de base. Ce type de base peut être un type scalaire quelconque, à l'exception d'un type réel. L'exemple qui suit illustre de quelle manière l'étendue d'un type peut être restreinte à un intervalle:

Delphi (J-C Armici janvier 2003 www.unvrai.com)

page 2-14

type lettre = 'A'..'Z'; entre_deux_guerres = 1919..1938; minuscules = 'a'..'z'; var

caractere : lettre; date : entre_deux_guerres; min : minuscules; fig. 2.4

Le type lettre est un intervalle défini par rapport au type caractère. Donc, seuls les caractères compris entre 'A' et 'Z' peuvent être affectés à la variable caractere. De même, la variable date pourra prendre uniquement des valeurs entières comprises entre 1919 et 1938. Les bornes de l'intervalle sont incluses dans l'ensemble des valeurs possibles. Le type intervalle est essentiellement utilisé dans deux buts. D'abord pour améliorer la lisibilité et la compréhension, mais également afin d'augmenter la fiabilité des programmes, car le Pascal détecte si une variable reçoit une valeur hors de l'intervalle déclaré. Dans l'exemple de la figure 2.4, il est sous-entendu que la variable min ne contiendra que des lettres minuscules. Si, au cours de l'exécution du programme, cette variable reçoit une autre valeur, une erreur sera signalée. Ce genre de problème devrait inciter le programmeur à revoir son programme et éventuellement à corriger une erreur de logique. Le type énuméré sert souvent de type de base au type intervalle. L'exemple qui suit en est une illustration: type jours week_end

= (lundi, mardi, mercredi, jeudi, vendredi, samedi, dimanche); = samedi..dimanche;

var aujourdhui : jours; conge : week_end; travail : lundi..vendredi; L'utilisation du type intervalle entre dans le cadre de la discipline que le programmeur s'impose pour faciliter une intervention ultérieure sur son programme. Cet effort supplémentaire est souvent récompensé par la réalisation de programmes plus sûrs et plus lisibles.

Conversion de types scalaires Les valeurs de type scalaire peuvent être converties en valeurs entières à l'aide de la fonction ord. Le Pascal offre également la possibilité d'effectuer la conversion d'une valeur de type scalaire en une valeur d'un autre type scalaire. Par exemple, considérons les déclarations suivantes: type couleurs formes var teinte figure entier

= = : : :

(jaune, bleu, rouge, vert); (carre, triangle, cercle); couleurs; formes; integer;

Il est possible d'effectuer des conversions de types en spécifiant l'identificateur du type désiré suivi d'un paramètre entre parenthèses. Ce paramètre doit être une valeur appartenant à un type scalaire connu ou déclaré: entier figure entier teinte figure

:= := := := :=

integer (rouge); formes (bleu); integer ('7'); couleurs (0); formes (0);

{ { { { {

= = = = =

2 triangle 55 jaune carre

} } } } }

Bien que cette conversion de types soit rarement utilisée, il convient de la signaler.

Delphi (J-C Armici janvier 2003 www.unvrai.com)

page 2-15

D'une manière générale, en Pascal, il n'est pas permis de mélanger différents types de données. Ce principe présente des exceptions lorsque deux types sont compatibles. C'est le cas, par exemple, pour les différents types entiers, qui sont compatibles à condition que les valeurs soient cohérentes par rapport à leur ensemble de définition. Les principes suivants régissent la compatibilité des types: •

les règles de cohérence doivent être respectées lors du mélange de données de type entier ou réel. Une expression mixte, comprenant des opérandes entiers et réels fournit un résultat de type réel;



le mélange de données de type numérique (entier ou réel) et de type caractère n'est pas autorisé.

Structures séquentielles Une structure séquentielle est une suite d'instructions qui s'exécutent les unes à la suite des autres, en séquence: begin temperature := 28; meteo.caption := 'Il fait chaud'; end;

Structures sélectives Ces structures permettent d'effectuer des choix selon des critères (ou conditions) que le programmeur a fixés. Ces instructions se comportent comme un aiguillage, à deux ou plusieurs branches. Selon qu'un critère est satisfait ou non, l'exécution du programme se poursuivra dans une "branche" ou dans une autre.

Instruction if Considérons l'exemple suivant: var temperature : integer; ... temperature := StrToInt (Edit1.text); if temperature > 20 then Label1.Caption := 'Il fait chaud' else Label1.Caption := 'Il fait froid'; ... fig. 2.5

Selon la valeur indiquée par l'utilisateur et placée dans la variable temperature, le programme affichera "Il fait chaud" ou bien "Il fait froid". On remarque également les trois mots réservés if, then et else utilisés dans cette structure sélective qui, traduite en français, s'exprimerait par: « si la température est supérieure à 20

alors afficher qu'il fait chaud sinon afficher qu'il fait froid »

L'exemple qui suit utilise une structure sélective sous une forme quelque peu différente: ... var revenu : real; taxes : real; ... Delphi (J-C Armici janvier 2003 www.unvrai.com)

page 2-16

taxes := revenu * 0.053; if revenu > 4000.0 then revenu := revenu - taxes; ... fig. 2.6

Traduite en français, la structure sélective signifie: « si le revenu est supérieur à 4000.0 alors on déduit 5,3 % du revenu » Dans l'exemple de la figure 2.5, le choix s'effectue entre deux instructions, l'une des deux étant forcément exécutée. Alors que dans le second exemple (figure 2.6) il s'agit d'exécuter ou de ne pas exécuter une instruction, à savoir la déduction des taxes. Une "instruction" peut être constituée par une seule instruction ou par un bloc d'instructions. Illustrons cela par l'exemple suivant: ... var revenu : real; taxes : real; ... if revenu > 4000.0 then begin revenu := revenu - (revenu * 0.053); msg := 'Le revenu subit des déductions.'); end else msg := 'Le revenu ne subit pas de déductions.'); ... Dans ce programme, deux instructions doivent être exécutées si le revenu est supérieur à 4000 francs. Il faut donc grouper ces deux instructions en un bloc délimité par begin et end. Ce concept est général en Pascal:

A chaque endroit d'un programme où une instruction peut figurer, il est possible de la remplacer par un bloc d'instructions. Comme une structure sélective est elle-même une instruction, on peut emboîter plusieurs structures if...then...else: if x > 0 then positif := positif + 1 else if x < 0 then negatif := negatif + 1 else zero := zero + 1; L'une des branches d'une instruction sélective peut donc contenir une autre instruction sélective. Ces structures emboîtées contribuent, dans le cas de l'instruction if...then, à rendre un programme moins lisible. L'instruction qui suit semble être ambiguë; à quel if se rapporte le else? if x > 0 then if y = 3 then z := y else z := x; En fait, les langages de programmation n'admettent pas les ambiguïtés. Dans notre exemple, l'ambiguïté est levée par une convention stipulant qu'un else se rapporte toujours au if précédent qui est le plus proche. Il convient, dans tous les cas, de soigner la présentation d'un programme; une indentation (décalage par rapport à la marge gauche) convenable augmente la lisibilité du programme. L'exemple précédent peut également s'écrire: if x > 0 then if y = 3 then z := y else z := x; Delphi (J-C Armici janvier 2003 www.unvrai.com)

page 2-17

ou encore: if x > 0 if y = z := else z :=

then 3 then y x;

Le Pascal permet de "court-circuiter" l'évaluation d'expressions booléennes contenant des opérateurs and et/ou or. Pour mieux comprendre l'importance de la manière dont une telle évaluation est effectuée, considérons l'instruction conditionnelle suivante: if (b 0) and (a / b > 4) then ... S'agissant d'une expression booléenne faisant intervenir un "et" logique, il suffit que l'un des deux membres de l'expression prenne la valeur "faux" pour que l'expression entière soit "fausse". Dans le cas où b possède la valeur 0, le Pascal n'évalue pas le second membre de l'expression, évitant ainsi une erreur à l'exécution due à une division par zéro. Cette particularité peut s'avérer fort utile dans certaines situations, et, dans tous les cas, elle augmente la vitesse d'exécution des programmes. Il est néanmoins possible d'éviter ce type de raccourci dans l'évaluation des expressions booléennes grâce à une directive fournie au compilateur.

Instruction case Considérons maintenant la partie d'un programme qui fait bouger un point sur l'écran. Supposons que l'utilisateur appuie sur les lettres 'H' pour "haut", 'B' pour "bas", 'D' pour "droite", 'G' pour "gauche". Cette partie de programme pourrait s'écrire de la manière suivante: ... if key='H' then y := y if key='B' then y := y if key='D' then x := x if key='G' then x := x im.canvas.pixels[x, y] ...

- 1; + 1; + 1; - 1; := clRed;

ou bien encore: ... if key = 'H' then y := y - 1 else if key = 'B' then y := y + 1 else if key = 'D' then x := x + 1 else if key = 'G' then x := x – 1; im.canvas.pixels[x, y] := clRed; ... Dans ces exemples, les structures sélectives se ressemblent et paraissent un peu lourdes dans leur notation. Le langage Pascal dispose d'une autre structure sélective permettant d'éviter dans certains cas de telles situations. Cette nouvelle structure correspond à l'instruction case ... of, et son utilisation permet de modifier l'exemple précédent en: ... case key of 'H' : y := y - 1; Delphi (J-C Armici janvier 2003 www.unvrai.com)

page 2-18

'B' : y := y + 1; 'D' : x := x + 1; 'G' : x := x - 1; end; im.canvas.pixels[x, y] := clRed; ... Cette nouvelle structure agit en fait à la manière d'un test à multiples branches. Pour chaque branche l'égalité entre la valeur de ch (appel‚ sélecteur) et la constante correspondante est vérifiée. Si effectivement la valeur de ch équivaut à une des constantes, l'instruction ou le bloc d'instructions correspondant sont exécutés. Si la valeur du sélecteur ne correspond à aucune des constantes indiquées l'exécution du programme se poursuit après la structure sélective. Cette structure est commune à toutes les implémentations de Pascal. Delphi offre cependant une variante parfois utile: une branche supplémentaire indiquée par un else permet l'exécution d'une instruction ou d'un bloc d'instructions au cas où la valeur du sélecteur ne correspond à aucune des constantes spécifiées. L'exemple qui suit illustre cette possibilité: ... case key of 'H' : y := y - 1; 'B' : y := y + 1; 'D' : x := x + 1; 'G' : x := x - 1; else msg := 'Erreur'; end; im.canvas.pixels[x, y] := clRed; ... Remarquons qu'avant le mot réservé else d'une structure case on trouve un point virgule Dans chaque branche d'une instruction case on peut indiquer plus d'une valeur, et même un intervalle de valeurs. L'exemple qui suit illustre cette possibilité: case a + b of 1 : a := b; 3..6 : b := a; 8, 9 : begin a := 0; b := 0; end; 10..13, 15 : b := 0; end;

Structures itératives Introduction Arrivés à ce point, nous ne sommes pas encore en mesure d'écrire un programme qui répète certaines instructions. Il nous manque les structures itératives qui permettent d'effectuer ce que l'on appelle communément des boucles. Dans le langage Pascal, on trouve trois types d'instructions répétitives. Les deux premières se distinguent par le fait que la condition de sortie est conséquente à l'évaluation d'une expression booléenne. La troisième est liée à l'évaluation implicite d'un compteur de boucle.

Instruction while

Delphi (J-C Armici janvier 2003 www.unvrai.com)

page 2-19

Cette structure permet de répéter l'exécution d'une instruction ou d'un bloc d'instructions tant qu'une expression est vérifiée: ... while nombre < 10000 do begin Edit1.text := Edit1.text + '1'; nombre := nombre * 10; end; ... fig. 2.7

Dans cet exemple, tant que le nombre est inférieur à 10000, le chiffre 1 est concaténé. Si le nombre vaut, par exemple, 20000 avant l'entrée dans la boucle, le programme n'effectue pas les instructions contenues dans la structure while. Il est donc possible que les instructions englobées par ce type de boucle ne soient jamais exécutées.

Instruction repeat Cette instruction permet de répéter l'exécution d'une instruction ou d'un bloc d'instructions jusqu'à ce qu'une condition soit vérifiée. En apparence, cette structure ressemble à la précédente. En réalité, la différence est significative et d'importance. Reprenons l'exemple de la figure 2.7 et voyons comment on pourrait l'écrire à l'aide d'une instruction repeat: ... repeat Edit1.text := Edit1.text + '1'; nombre := nombre * 10; until nombre >= 10000; ... fig. 2.8

Le fonctionnement des programmes illustrés sur les figures 2.7 et 2.8 est pratiquement identique. La différence fondamentale concerne l'emplacement de la condition par rapport au contenu de la boucle. Dans le second cas, même si le nombre est déjà supérieur à 10000, les instructions contenues dans la structure repeat seront exécutées une fois. La condition qui détermine l'éventuel arrêt de la répétition (nombre >= 10000) se trouvant à la fin de la boucle, l'entrée dans la structure est obligatoire. De plus, dans l'exemple de la figure 2.8, l'expression booléenne constituant la condition d'arrêt est la négation logique de l'expression booléenne de la figure 2.7. En effet, dans le cas d'une boucle while, il s'agit d'une condition de continuation.

Instruction for Cette troisième structure répétitive est utilisée lorsque le nombre d'itérations est connu. Il faut spécifier l'identificateur d'une variable appelée indice dont la valeur est modifiée implicitement au cours de l'exécution de la boucle, la première valeur prise par cette variable ainsi que la valeur pour laquelle la répétition s'arrête. A chaque passage dans la boucle, l'indice prend la valeur suivante ou précédente (selon si la boucle est ascendante ou descendante). Le programme qui suit affiche les carrés des nombres entiers compris entre 1 et 10: ... const max = 10; var nombre : integer; begin for nombre := 1 to max do liste.items.add (IntToStr (nombre * nombre)); end;

Delphi (J-C Armici janvier 2003 www.unvrai.com)

page 2-20

Les résultats fournis par ce programme se présentent sous la forme d'un ListBox dont le contenu est: 1 4 9 16 25 36 49 64 81 100 Le mot réservé to peut être remplacé par downto afin que la boucle soit parcourue dans l'ordre décroissant de l'indice (dans l'exemple, la variable nombre). La boucle s'écrit alors: for nombre := max downto 1 do .... et les résultats seront: 100 81 .. Il n'est pas possible, en Pascal, d'indiquer explicitement un pas d'incrémentation de la boucle. En indiquant to, l'indice prend la valeur suivante, alors qu'en utilisant downto il prend la valeur précédente. Pour un pas différent, il convient d'utiliser une boucle while ou repeat. Le Pascal ne permet pas non plus d'utiliser la boucle for avec un indice de type réel. Il faudra dans ce cas aussi faire appel à une boucle while ou repeat. En revanche, l'indice d'une boucle for peut être de type entier, énuméré, booléen ou caractère. Lorsqu'une instruction for se trouve dans une procédure ou une fonction, la variable constituant l'indice de la boucle doit obligatoirement être déclarée localement à la procédure ou à la fonction.

Procédure Halt La procédure Halt provoque une fin anormale d'un programme et passe le contrôle au système d'exploitation. Pour provoquer la fin normale d'une application il convient d'utiliser Application.Terminate. On peut facultativement passer un nombre entier comme paramètre à la procédure Halt, comme dans l'exemple qui suit. Il s'agit d'un code de sortie transmis au système d'exploitation. Ce code est laissé au choix du programmeur. begin if 1 = 1 then if 2 = 2 then if 3 = 3 then Halt(1); { On quitte le programme } Form1.caption := 'Ce code ne sera jamais exécuté'; end;

Delphi (J-C Armici janvier 2003 www.unvrai.com)

page 2-21

Procédure Break La procédure Break provoque l'interruption d'une boucle for, while ou repeat. L'exemple qui suit montre une procédure qui teste si un nombre nb est premier. Elle comporte une boucle for dans laquelle on détermine si le nombre nb est visible par les nombres compris entre 2 et nb - 1. Dès que l'un de ces nombres divise nb cela signifie que nb n'est pas premier. Il est donc inutile de poursuivre l'exécution de la boucle, d'où l'utilisation de la procédure Break. procedure TForm1.testClick(Sender: TObject); var nb : integer; // nombre à tester i : integer; // indice de boucle premier : boolean; begin premier := true; nb := strtoint (nombre.text); for i := 2 to nb - 1 do begin if nb mod i = 0 then begin premier := false; break; end; end; if premier then resultat.text := 'Nombre premier' else resultat.text := 'Nombre non premier'; end;

Procédure Continue L'appel à la procédure Continue provoque le passage du contrôle de l'exécution à l'itération suivante dans une instruction for, while ou repeat. procedure traitement; var i : integer; begin for i := 0 to MAX do begin if t[i] = 0 then continue; { on passe à l'itération suivante } ... { ici les instructions de la boucle si t[i] 0 } ... end; end;

Procédure Exit La procédure Exit permet de quitter l'exécution de la procédure en cours. Si la procédure en cours correspond au programme principal, Exit termine l'exécution du programme. L'utilisation de la procédure Exit permet souvent de rendre le programme plus clair en évitant la lourdeur des clauses else. L'utilisation de la procédure Exit dans le fragment de programme suivant permet, entre autre de ne pas utiliser de else et ne nuit aucunement à la lisibilité du code, au contraire:

Delphi (J-C Armici janvier 2003 www.unvrai.com)

page 2-22

procedure TForm1.AmisClick(Sender: TObject); var nb : integer; divnb1 : integer; divnb2 : integer; min, max : integer; begin if strtoint (inf.text) >= strtoint (sup.text) then begin showmessage ('Limite supérieure > limite inférieure SVP'); exit; end; listbox1.clear; min := strtoint (inf.text); max := strtoint (sup.text); for nb := min to max do begin divnb1 := SommeDiviseurs (nb); divnb2 := SommeDiviseurs (divnb1); if (divnb2 = nb) then listbox1.items.add (inttostr(nb) + ' et ' + inttostr(divnb1) + ' sont amis'); end; end;

Instruction goto Cette instruction correspond à un branchement inconditionnel et permet de rompre une séquence en poursuivant l'exécution d'un programme à une autre instruction que la suivante. L'emplacement de l'instruction de branchement est repéré par une étiquette, qui n'est autre qu'un identificateur ou un numéro compris entre 0 et 9999. Une étiquette doit être déclarée après le mot réservé label dans la partie réservée aux déclarations. Son domaine de définition correspond au bloc où elle est déclarée; il n'est donc pas possible d'effectuer des branchements par l'instruction goto entre procédures ou fonctions. Si une déclaration d'étiquette figure dans un programme, cette étiquette doit impérativement être utilisée. L'exemple qui suit montre comment s'utilise cette instruction: procedure demo; label ici; ... begin if Label1.left > 150 then goto ici else Label1.caption := 'Fin'; ... ici: ... end; Ce programme est bien entendu construit de manière aberrante. Il suffit de l'écrire comme suit pour que l'instruction goto n'ait plus de raison d'être: procedure demo; ... begin if Label1.left y then max := x else max := y; edit1.text := floattostr(max); end; procedure TForm1.Button1Click(Sender: TObject); begin x := 12; y := 23; Maximum; end; Dans ce programme, la procédure utilise uniquement des variables globales, déclarées dans l'entête de l'unité. Ceci présente un gros inconvénient, car la procédure maximum peut traiter uniquement les variables x et y. Elle n'est donc pas assez générale.

Transfert de paramètres par valeur var Form1: TForm1; x, y, max : real; implementation {$R *.DFM} procedure TForm1.maximum (a, b: real); begin if a > b then max := a else max := b; edit1.text := floattostr(max); end; procedure TForm1.Button1Click(Sender: TObject); Delphi (J-C Armici janvier 2003 www.unvrai.com)

page 3-10

begin x := 12; y := 23; Maximum (x, y); end; Dans ce programme, les paramètres formels a et b reçoivent lors de l'appel les valeurs contenues dans les paramètres réels x et y.

Transfert de paramètres par valeur et par référence var Form1: TForm1; x, y, grand : real; implementation {$R *.DFM} procedure TForm1.maximum (a, b: real; var max: real); begin if a > b then max := a else max := b; end; procedure TForm1.Button1Click(Sender: TObject); begin x := 12; y := 23; Maximum (x, y, grand); edit1.text := floattostr(grand); end; Dans ce dernier exemple, la procédure reçoit les deux valeurs à comparer par l'intermédiaire des paramètres d'entrée a et b. Elle détermine ensuite la plus grande des deux valeurs (max) et la transmet au programme appelant. Cette dernière phase fait appel au transfert par référence, max étant un paramètre de sortie. La procédure maximum décrite dans le dernier des trois exemples est la plus proche de l'esprit de la programmation structurée. Dans cette optique, une procédure doit être indépendante et ses échanges avec l'extérieur doivent passer, dans la mesure du possible, par un transfert de paramètres. Lorsque ces conditions sont remplies la procédure est autonome, générale et plus fiable. Il serait, par exemple, très facile de reprendre la procédure maximum du troisième exemple dans un programme totalement différent. Son utilisation implique uniquement la connaissance de ses paramètres et de leur signification. Voici quelques avantages liés à l'utilisation des procédures: -

mise en pratique aisée des concepts de la programmation structurée et de la conception descendante;

-

rentabilisation du travail accompli. Lorsqu'un problème ou une partie de problème a déjà été résolu, par soi-même ou par d'autres, il ne faut pas repartir de zéro. La tendance est plutôt de constituer des bibliothèques de procédures et de fonctions, groupées, par exemple, en unités. Cette méthode évite de récrire des routines semblables dans chaque nouveau programme et est largement répandue dans le domaine de la programmation. Plus un programmeur est expérimenté, plus sa bibliothèque de sous-programmes (procédures, fonctions et unités) grandit, plus ses nouveaux programmes sont réalisés rapidement;

-

gain de place. Lorsque la même séquence d'instructions apparaît à plusieurs emplacements d'un programme, il convient d'en faire une procédure. De plus, la paramétrisation d'une procédure permet de traiter des problèmes analogues, mais pas forcément identiques.

Delphi (J-C Armici janvier 2003 www.unvrai.com)

page 3-11

-

Enfin, lorsque les programmes font appel à des algorithmes récursifs, l'utilisation de procédures ou de fonctions est indispensable.

3.7 Autres modes de passage de paramètres Les passages de paramètres par valeur ou par référence sont les plus fréquemment utilisés. Delphi propose d'autres possibilités concernant le passage de paramètres à une procédure.

Paramètres "constantes" Lors d'un passage de paramètre par valeur, bien que qu'aucune information ne sort de la procédure, celle-ci peut utiliser et modifier l'information quelle reçoit. Elle utilise alors le paramètre formel comme une variable locale. Dans le but d'éviter cette possibilité il est possible de faire appel à des paramètres "constantes" dont voici un exemple d'utilisation: procedure compare (const t1, t2: integer); Dans la procédure compare les paramètres t1 et t2 ne peuvent pas être modifiés.

Paramètres de sortie Un paramètre de sortie (précédé de out) est transmis par référence comme un paramètre précédé de var. Toutefois, avec un paramètre de sortie, la valeur initiale de la variable référencée est ignorée par la procédure à laquelle elle est transmise. Le paramètre out n'est utilisé qu'en sortie; il indique simplement à la procédure où placer la valeur de sortie, sans spécifier la valeur d'entrée. Dans l'exemple suivant:

Delphi (J-C Armici janvier 2003 www.unvrai.com)

page 3-12

procedure TForm1.maximum (a, b: real; out max: real); begin if a > b then max := a else max := b; end; il est clair que max est un paramètre de sortie. En effet, on ne connaît pas sa valeur lors de l'appel de la procédure maximum.

Paramètres sans type Seuls les paramètres précédés de var, out et const peuvent ne pas avoir de type (alors que les paramètres passés par valeur doivent toujours avoir un type). Dans le corps d'une procédure les paramètres sans type sont incompatibles avec tous les types. De ce fait, ils doivent être utilisés uniquement en mode transtypage comme dans l'exemple qui suit: function Egal (var Src, Dest; Taille: Integer): Boolean; type TOctets = array[0..MaxInt - 1] of Byte; var N: Integer; begin N := 0; while (N < Taille) and (TOctets(Dest)[N] = TOctets(Src)[N]) do Inc(N); Egal := N = Taille; end;

3.8 Fonctions Les notions abordées dans les sections précédentes, liées aux procédures, sont également valables pour les fonctions. La structure et l'emploi de ces deux types d'objets est très semblable. Voici toutefois quelles en sont les différences essentielles: •

le mot réservé function remplace le mot réservé procedure lors de la déclaration d'une fonction;



par analogie avec la notion mathématique, les paramètres (arguments) d'une fonction ne devraient pas être modifiés. Toutefois le transfert de paramètres par référence à une fonction est admis en Pascal;



une fonction fournit un résultat de type scalaire. Ce type est spécifié, lors de la déclaration de l'en-tête, par le signe ":" suivi de l'identificateur de type; cette indication est placée après le nom de la fonction et la liste de paramètres éventuelle;



dans le corps d'une fonction, on doit affecter au moins une fois une valeur à l'identificateur de la fonction ou à la variable result;



contrairement à l'appel d'une procédure qui, lui, est considéré comme une instruction, l'appel à une fonction est considéré comme opérande d'une expression. Cet opérande, ou l'expression qui le contient, peuvent être affectés à une variable ou bien imprimés.

La manière dont une fonction doit être déclarée est illustrée par l'exemple qui suit. La fonction tg fournit la valeur de la tangente d'un angle exprimé en radians: function tg (x: real): real; begin tg := sin (x) / cos (x); Delphi (J-C Armici janvier 2003 www.unvrai.com)

page 3-13

end; Dans un programme contenant cette déclaration, on pourrait, par exemple, écrire les instructions suivantes: z := tg (alpha); ctg := 1 / tg (y); Resultat.text := 'Tangente = ' + floattostr(tg(y));

Remarque L'avantage d'affecter le résultat d'une fonction à la variable result plutôt qu'à l'identificateur de la fonction est de pouvoir s'en servir sans risque de récursivité au sein de la fonction, comme dans l'exemple suivant: function tracer (x: real): real; begin result := 3*x-2; if result < 0 then result := abs (result) end;

Delphi (J-C Armici janvier 2003 www.unvrai.com)

page 3-14

Chapitre 4

CHAINES DE CARACTERES

A la fin de cette partie, vous serez capable de: • Utiliser à bon escient les différents types de chaînes de caractères; • Utiliser les différentes routines de traitement des chaînes de caractères;

4.1 Introduction Depuis plusieurs années, les applications numériques ne sont plus le seul domaine de prédilection de l'informatique. L'utilisation de logiciels devient toujours plus conviviale et naturelle. Le traitement d'informations non-numériques s'avère donc nécessaire en programmation. Parmi les types de données non-numériques nous avons déjà examiné les types booléen, caractère et énuméré. Mais lorsqu'un programme traite des mots, des phrases, ou plus généralement du texte, il faut pouvoir disposer d'un nouveau type de données, d'un emploi souple et efficace. Le type chaîne de caractères (string) répond à ces critères. Il est absent des premières implémentations de Pascal, y compris celle de N. Wirth. Mais actuellement tous les langages Pascal, dont Delphi, autorisent ce type de données, et disposent de plusieurs procédures et fonctions standard qui lui sont associées.

4.2 Type chaîne de caractères (string) Une chaîne de caractères est une suite de caractères. Une variable de ce type est déclarée par le mot string suivi de la longueur maximale de la chaîne de caractères, entre crochets. Lorsque le mot string apparaît seul, une longueur maximale n'est pas déterminée: var nom prenom adresse etudes

: : : :

string[25]; string[30]; string[50]; string; fig. 4.1

A l'aide de ces déclarations, les affectations qui suivent sont possibles: nom := 'Dupont'; adresse := '36, chemin d''enfer'; prenom := ''; etudes := 'Lettres'; Le contenu d'une chaîne de caractères doit être placé entre apostrophes. Si une apostrophe doit figurer dans une chaîne de caractères, il faut la doubler. Il n'y a pas de restrictions concernant les caractères qui peuvent figurer dans une chaîne de caractères. En revanche, il faut veiller à ne pas dépasser le nombre de caractères maximum figurant dans la déclaration, faute de quoi l'excédent est tronqué.

Analysons les caractéristiques de la variable nom après l'affectation qui suit:

Delphi (J-C Armici janvier 2003 www.unvrai.com)

page 4-1

nom := 'Pam'; La longueur physique vaut 25 et correspond au nombre figurant entre crochets lors de la déclaration (voir figure 4.1). La longueur logique est le nombre de caractères effectivement occupés dans la variable nom. Cette longueur vaut 3. Les types chaîne de caractères et caractère ne sont pas entièrement compatibles. En particulier, l'affectation d'un caractère à une chaîne de caractères est possible, la chaîne aura alors une longueur de 1. var adresse : string[10]; lettre : char; ... lettre := 'G'; adresse := lettre; ... En revanche, l'affectation d'une chaîne de caractères, même de longueur 0 ou 1, à une variable de type caractère n'est pas autorisée. L'affectation suivante, relative à la variable lettre, n'est donc pas admise: var adresse : string[10]; lettre : char; ... adresse := '4'; { initialisation } lettre := adresse; { affectation incorrecte } ... De plus, si une chaîne de caractères est affectée à une deuxième chaîne plus courte une troncation est effectuée. var mot1 : string[20]; mot2 : string[10]; ... mot1 := 'La prochaine fois'; mot2 := mot1; { mot2 contient la chaîne 'La prochai' } ... Enfin, des expressions booléennes peuvent être construites à l'aide de chaînes de caractères et d'opérateurs relationnels: nom < 'Dupont' prenom = 'Nestor' adresse '' Les comparaisons de chaînes de caractères se font d'après l'ordre lexicographique, en suivant le code ASCII des caractères. Avec cette convention, la chaîne de caractères 'Dupont' est inférieure à la chaîne de caractères 'dupont'. Il en est de même pour 'abc' et 'abcdef', ainsi que pour 'Z' et 'a' ! En effet, le code de 'Z' est 90, alors que celui de 'a' est 97.

4.3 Procédures et fonctions relatives aux chaînes de caractères Dans cette section, nous allons examiner les procédures et fonctions standard les plus courantes concernant les chaînes de caractères (d'autres procédures et fonctions seront abordées plus loin). Elles sont données par ordre alphabétique. Concat

syntaxe: concat (st1, st2,..., stn)

Cette fonction permet de concaténer deux ou plusieurs chaînes de caractères. Des constantes ou variables de type caractère peuvent également figurer dans la liste de paramètres de cette fonction. Le nombre de paramètres est variable, mais il faut éviter que la longueur de la chaîne Delphi (J-C Armici janvier 2003 www.unvrai.com)

page 4-2

résultante ne dépasse la longueur déclarée pour la variable à laquelle elle est affectée. Les instructions qui suivent illustrent le fait de pouvoir concaténer indifféremment des caractères ou des chaînes de caractères: procedure TForm1.Button1Click(Sender: TObject); var st1 : string[30]; resultat : string[60]; begin st1 := 'Voici un passage '; resultat := concat (st1, chr(13), 'à la ligne'); label1.caption := resultat; end; Dans cet exemple un clic sur le bouton affiche le contenu de la variable resultat dans un Label:

Le TURBO Pascal dispose d'une autre forme de concaténation. Elle utilise l'opérateur de concaténation "+" et se présente de la manière suivante: ... st1 := 'Voici un passage '; resultat := st1 + chr(13) + 'à la ligne'; label1.caption := resultat; ... Ces instructions produisent le même résultat que celles de l'exemple précédent. A noter que chr(13) correspond au caractère "retour de chariot". Copy

syntaxe: copy (st, position, nbre)

Cette fonction retourne une chaîne de caractères extraite de la chaîne st; la position du premier caractère à extraire est fournie par position, alors que nbre indique le nombre de caractères à extraire. Les paramètres position et nbre doivent être de type entier. st := 'Ceci est une chaîne très longue'; st2 := copy (st, 1, 19); Edit1.text := st2; L'exécution de ces instructions place la chaîne de caractères 'Ceci est une chaîne' dans Edit1.

Delete

syntaxe: delete (st, position, nbre)

Cette procédure supprime un ou plusieurs caractères dans une chaîne de caractères, à partir d'une position donnée; st est l'identificateur de la variable contenant la chaîne de caractères, position est la position du premier caractère à supprimer et nbre est le nombre de caractères à supprimer. Les paramètres position et nbre doivent être de type entier. st := 'Ceci est un petit exemple'; delete (st, 13, 6); Delphi (J-C Armici janvier 2003 www.unvrai.com)

page 4-3

Edit1.text := st; L'exécution de ces instructions place la chaîne de caractères 'Ceci est un exemple' dans Edit1. Insert

syntaxe: insert (st1, st2, position)

Cette procédure insère la chaîne de caractères st1 dans la chaîne de caractères st2 à partir de la position position (dans st2). Les paramètres st1 et st2 sont du type chaîne de caractères et position est de type entier. St1 := 'de'; St2 := 'Delphi Borland'; insert (st1, st2, 8); Edit1.text := st2; L'exécution de ces instructions place la chaîne de caractères 'Delphi de Borland' dans Edit1. Length

syntaxe: length (st)

Cette fonction fournit la longueur logique de la chaîne de caractères st, c'est-à-dire le nombre de caractères qu'elle contient. st := 'Cette phrase contient 26 lettres'; longueur := length(st); L'exécution de ces instructions place le nombre 32 dans la variable longueur. Pos

syntaxe: pos (s_st, st)

Cette fonction permet de déterminer si la chaîne de caractères s_st est contenue dans la chaîne st. Si c'est le cas, la fonction retourne la position où commence la chaîne s_st, à l'intérieur de la chaîne st. Si la chaîne s_st est absente de la chaîne st, le résultat est 0. st := 'un deux trois quatre cinq'; pos1 := pos ('trois', st); pos2 := pos ('six', st); Après l'exécution de ces instructions pos1 contient le nombre 9, alors que pos2 contient le nombre 0.

Str

syntaxe: str (valeur, st)

Cette procédure effectue la conversion de la valeur numérique contenue dans valeur en une chaîne de caractères st. Le paramètre valeur est de type entier ou réel et peut être suivi d'un paramètre d'écriture (dans notre exemple, on désire un nombre formaté sur 5 caractères au total, dont 1 décimale). nombre := 456.34; str (nombre:5:1, st); Edit1.text := st; L'exécution de ces instructions place la chaîne de caractères '456.3' dans Edit1.

Delphi (J-C Armici janvier 2003 www.unvrai.com)

page 4-4

Val

syntaxe: val (st, variable, code)

Le but de cette procédure est de convertir la chaîne de caractères st en une variable numérique. Pour que cette conversion soit effective, le contenu de la chaîne de caractères doit correspondre aux règles d'écriture des nombres; de plus, aucun espace ne doit se trouver en première ou en dernière position. Le paramètre variable peut être de type entier ou réel. Après l'appel à cette procédure, si la conversion a pu être effectuée, la variable numérique code contient la valeur 0. Dans le cas contraire, la variable code contient la position du premier caractère de la chaîne st qui empêche la conversion, et le contenu de variable n'est pas défini. st := '12.7'; val (st, nombre, code); if code = 0 then Edit1.text := floattostr(nombre) + ' else Edit1.text :=inttostr(code);

' + inttostr(code)

Le résultat de l'exécution de ces instructions place les nombres 12.7 et 0 dans Edit1. En revanche, si st avait été initialisé à '12a.7' l'exécution des instructions aurait placé le nombre 3 dans Edit1, correspondant à la position du caractère incorrect: Remarque: Il est possible d'accéder aux différents caractères d'une chaîne de caractères en indiquant, entre crochets, la position du caractère désiré: ... var st : string[10]; i : integer; ... st := 'remarque'; for i := 1 to length (st) do Label1.caption := Label1.caption + st[i] + chr(13); ... Le résultat de l'exécution de ces instructions est l'affichage du mot "remarque" dans Label1, verticalement, un caractère par ligne.

4.4 Quelques précisions concernant les chaînes de caractères Le type string est un type générique. En fait, Delphi gère trois types de chaînes de caractères: Type

Longueur max.

chaînes courtes, compatibilité ascendante

caractères

4 octets à 2 Goctets

caractères 8 bits (ANSI)

caractères

4 octets à 2 Goctets

caractères Unicode

255 caractères

AnsiString

env. 2

WideString

30

env 2

Utilisation

2 à 256 octets

ShortString

31

Mémoire nécessaire

Dans les versions actuelles de Delphi l'indication du type string correspond, par défaut, au type AnsiString. Dans les cas où, pour des raisons de compatibilité avec d'anciennes versions de Delphi (J-C Armici janvier 2003 www.unvrai.com)

page 4-5

Delphi ou de Pascal, on doit utiliser des chaînes courtes, il convient d'utiliser explicitement le type ShortString dans les déclaration, comme dans: var nom : ShortString; Dans la plupart des cas il est préférable d'utiliser le type générique string pour traiter des chaînes de caractères. Comme on peut le voir sur dans le tableau ci-dessus, une variable de type string (AnsiString) peut occuper, en mémoire, de 4 octets à 2 Goctets. Comme 2 Goctets représente une taille considérable, dans la pratique, la taille maximale est limitée par la quantité de mémoire disponible. Le type WideString est analogue au type string (AnsiString) à la différence que chaque caractère de la chaîne est stocké sur deux octets (représentation Unicode) au lieu d'un. Pour une utilisation courante et un jeu de caractères standard (occidental), il est inutile d'utiliser le type WideString. Remarques: •

L'apparition de nouveaux types de chaînes de caractères a été dictée par l'évolution des systèmes d'exploitation avec le support des caractères Unicode, ainsi que par le besoin de plus en plus fréquent d'utiliser de longues chaînes de caractères.



Ces différents types peuvent sans autre être combinés dans les affectations et les expressions. Le compilateur prend en charge les conversions nécessaires de manière automatique et transparente.



La gestion de la place mémoire occupée par les variables de type string s'effectue de manière dynamique. De ce fait la déclaration suivante: var nom : String; ne signifie pas que la place mémoire réservée à la variable nom est de 2 Goctets. En fait, la taille de la mémoire allouée à la variable nom varie en fonction du nombre de caractères contenus dans cette variable.



Nous n'aborderons pas dans ce chapitre la manière dont sont stockées et représentées de manière interne les chaînes de caractères de type string (AnsiString).



Quelques nuances, que nous n'aborderons pas ici, concernant la représentation interne ainsi que le traitement par le compilateur distinguent les types AnsiString et WideString.



La VCL (Visual Component Library) de Delphi ne fait pas appel aux chaînes de type WideString.

Pchar et tableaux de caractères Delphi supporte des chaînes de caractères appelées les chaînes à zéro terminal (AZT). Ces chaînes sont largement utilisées par les langages de programmation C et C++, et par Windows luimême. Grâce au fait que Delphi supporte les chaînes à zéro terminal, ainsi que des fonctions de gestion de ces chaînes (dans l'unité SysUtils), il est facile d'interfacer un programme Delphi avec d'autres langages ou avec l'API Windows. Une chaîne AZT est constituée d'une suite de caractères non null, suivis d'un caractère null (code ASCII #0). Ces chaînes n'ont pas d'indicateur de longueur séparé; le premier caractère NULL d'une chaîne AZT marque la fin de cette chaîne. Une chaîne AZT se déclare de la manière suivante: array[0..N] of Char

Un tableau de caractères à base zéro (dont le début commence à l'indice zéro) est compatible avec le type PChar. Cela signifie que partout où un PChar est attendu, il est possible d'utiliser à la place un tableau de caractères à base zéro. Delphi (J-C Armici janvier 2003 www.unvrai.com)

page 4-6

Lorsqu'un tableau de caractères à base zéro est utilisé à la place d'une valeur PChar, le compilateur convertit le tableau de caractères en une constante pointeur dont la valeur correspond à l'adresse du premier élément du tableau. Chaînes longues et chaînes à zéro terminal La mémoire allouée pour une chaîne longue est terminée par un caractère null (qui ne fait pas partie de la chaîne, mais qui est stocké immédiatement après le dernier caractère de la chaîne). Grâce à cette marque de fin de chaîne, il est possible de transtyper une variable de type AnsiString en variable de type Pchar (pointeur vers une chaîne de caractères). Il suffit d'écrire PChar(S), où S est une variable AnsiString. Pchar(S) fournit un pointeur vers le premier caractère d'une chaîne longue. Voici un exemple montrant cette possibilité. Si nous voulons afficher une boîte de message à l'aide des instructions suivantes: var libelle : string; begin libelle := 'Bonjour'; messagebox (0, libelle, 'Message', mb_ok); end;

une erreur se produit et le compilateur signale: "Types incompatibles 'String' et 'Pchar'". Il suffit alors d'effectuer la modification suivante pour que tout rentre dans l'ordre: var libelle : string; begin libelle := 'Bonjour'; messagebox (0, Pchar(libelle), 'Message', mb_ok); end;

Cet exemple montre en fait comment passer des chaînes longues à une fonction qui attend des paramètres chaîne à zéro terminal. Ce type de transtypage est très courant et même essentiel lorsque l'on doit appeler des fonctions de l'API de Windows qui, rappelons-le, sont entièrement écrites en langage C.

4.5 Routines de traitement des chaînes de caractères Vous trouverez dans les tableaux qui suivent une description succincte des principales fonctions et procédures proposées par Delphi pour la gestion des chaînes de caractères. Toutes ces routines sont directement disponibles dans Delphi. Rappelons que l'on peut se procurer des librairies de routines en tout genre permettant de compléter l'assortiment proposé par Delphi.

Routines de traitement de chaînes de type string (chaînes Pascal) Fonction

Description

AdjustLineBreaks

Transforme les ruptures de lignes dans une chaîne en séquences CR/LF.

AnsiCompareStr

Comparaison, en tenant compte des majuscules/minuscules, de deux chaînes.

AnsiCompareText

Comparaison, sans tenir compte des majuscules/minuscules, de deux chaînes.

AnsiLowerCase

Convertit des caractères en minuscules.

AnsiUpperCase

Convertit des caractères en majuscules.

CompareStr

Comparaison, en tenant compte des majuscules/minuscules, de deux chaînes.

CompareText

Comparaison, sans tenir compte des majuscules/minuscules, de deux

Delphi (J-C Armici janvier 2003 www.unvrai.com)

page 4-7

chaînes. Concat

Concatène une suite de chaînes.

Copy

Renvoie une sous-chaîne d'une chaîne.

Delete

Efface une sous-chaîne d'une chaîne.

DisposeStr

Libère une chaîne du tas.

FmtLoadStr

Charge une chaîne dans la ressource table de chaînes d'un programme.

Insert

Insère une sous-chaîne dans une chaîne.

IntToHex

Convertit un entier en hexadécimal.

IntToStr

Convertit un entier en chaîne.

IsValidIdent

Renvoie True si la chaîne spécifiée est un identificateur valide.

Length

Renvoie la longueur dynamique de la chaîne.

LoadStr

Charge la ressource chaîne depuis le fichier exécutable de l'application.

LowerCase

Met en minuscules la chaîne spécifiée.

NewStr

Alloue une nouvelle chaîne dans le tas.

Pos

Recherche une sous-chaîne dans une chaîne.

Str

Convertit une valeur numérique en chaîne.

StrToInt

Convertit une chaîne en entier.

StrToIntDef

Convertit une chaîne en entier ou à une valeur par défaut.

Trim

Supprime les espaces de début et de fin et les caractères de contrôle d'une chaîne donnée.

TrimLeft

Supprime les espaces de début et les caractères de contrôle d'une chaîne donnée

TrimRight

Supprime les espaces de fin et les caractères de contrôle d'une chaîne donnée.

UpperCase

Met en majuscules la chaîne spécifiée.

Val

Convertit une valeur chaîne en sa représentation numérique.

Routines de traitement de chaîne AZT Fonction

Description

StrAlloc

Alloue une zone tampon d'une taille donnée sur le tas.

StrBufSize

Renvoie la taille d'une zone tampon de caractère allouée en utilisant StrAlloc ou StrNew.

StrCat

Concatène deux chaînes.

StrComp

Compare deux chaînes.

StrCopy

Copie une chaîne.

StrDispose

Dispose une zone tampon caractère allouée en utilisant StrAlloc ou StrNew.

StrECopy

Copie une chaîne et renvoie un pointeur à la fin de la chaîne.

StrEnd

Renvoie un pointeur à la fin d'une chaîne.

Delphi (J-C Armici janvier 2003 www.unvrai.com)

page 4-8

StrFmt

Formate une ou plusieurs valeurs dans une chaîne.

StrIComp

Compare deux chaînes sans tenir compte des majuscules/minuscules.

StrLCat

Concatène deux chaînes avec une longueur maximum donnée de la chaîne résultante.

StrLComp

Compare deux chaînes pour une longueur maximum donnée.

StrLCopy

Copie une chaîne jusqu'à une longueur maximum donnée.

StrLen

Renvoie la longueur d'une chaîne.

StrLFmt

Formate une ou plusieurs valeurs dans une chaîne avec une longueur maximum donnée.

StrLIComp

Compare deux chaînes pour une longueur maximum donnée sans tenir compte des majuscules/minuscules.

StrLower

Convertit une chaîne en minscules.

StrMove

Déplace un bloc de caractères d'une chaîne sur l'autre.

StrNew

Alloue une chaîne sur le tas.

StrPCopy

Copie une chaîne Pascal vers une chaîne à zéro terminal.

StrPLCopy

Copie une chaîne Pascal vers une chaîne AZT avec une longueur maximum donnée.

StrPos

Renvoie un pointeur sur la première occurrence d'une sous-chaîne donnée dans une chaîne.

StrRScan

Renvoie un pointeur sur la dernière occurrence d'un caractère donné dans une chaîne.

StrScan

Renvoie un pointeur sur la première occurrence d'un caractère donné dans une chaîne.

StrUpper

Convertit une chaîne en majuscules.

Exercice 4.1 Ecrire un programme dans lequel l'utilisateur peut taper un texte dans un Edit. Lorsque l'utilisateur clique sur le bouton "Encadrer" le programme doit afficher la chaîne de caractères dans un Label, encadrée par des astérisques. Il convient de prendre un Label assez haut pour contenir au moins trois lignes. Pour une bonne présentation du résultat, il convient également de choisir une police de caractères non proportionnelle pour le Label. Voici l'aspect du programme:

Delphi (J-C Armici janvier 2003 www.unvrai.com)

page 4-9

Exercice 4.2 Ecrire un programme qui agit sur une phrase en remplaçant toutes les séquences de deux ou plusieurs espaces par un seul espace, et affiche la phrase obtenue. Afin de mieux visualiser les espaces, il est préférable de choisir une police de caractères non proportionnelle. Voici l'aspect du programme:

Exercice 4.3 Ecrire un programme qui affiche dans un Listbox les mots d'une phrase introduite par l'utilisateur. Chaque ligne du Listbox doit contenir un mot de la phrase. On considère que les mots sont séparés par un ou plusieurs espaces. L'instruction qui permet d'ajouter une ligne dans un Listbox est: Listbox.Items.Add (mot);

// mot est la chaîne à ajouter

Voici l'aspect du programme:

Delphi (J-C Armici janvier 2003 www.unvrai.com)

page 4-10

Exercice 4.4 Ecrire un programme qui calcule la fréquence d'apparition des voyelles dans un texte que l'utilisateur fournit. Ce programme affiche ensuite un histogramme sous la forme suivante:

Le texte est placé dans un Memo (en fait dans Memo.text). L'histogramme est constitué de six Labels ayant une couleur se distinguant du fond gris de la fenêtre et dont la largeur varie en fonction de la fréquence d'apparition des voyelles.

Delphi (J-C Armici janvier 2003 www.unvrai.com)

page 4-11

Améliorer ce programme en ajoutant les fonctionnalités suivantes: •

afficher le nombre d'apparition de chaque voyelle



effectuer des tests pour éviter que les barres de couleur ne sortent de la fenêtre



au cas où une barre de couleur atteint le bord droit de la fenêtre, effectuer une renormalisation qui réduit proportionnellement la largeur de toutes les barres.

Delphi (J-C Armici janvier 2003 www.unvrai.com)

page 4-12

Chapitre 5

TYPES STRUCTURES

A la fin de cette partie, vous serez capable de: • Utiliser à bon escient les types structurés ensemble, tableau, enregistrement; • Utiliser les composants Listbox, Memo et StringGrid dans des applications Delphi;

5.1 Introduction Les types de données que nous avons rencontrés jusqu'ici étaient des types scalaires (ou non structurés), c'est-à-dire élémentaires. Parmi ces types nous avons distingué les types prédéfinis de ceux que l'utilisateur peut lui-même définir. Dans ce chapitre, nous allons étudier les types structurés qui constituent une des richesses de la représentation des données en Pascal. Un type est structuré s'il est composé de plus d'un élément. C'est le cas des types suivants: •

ensembles



tableaux



enregistrements



fichiers

Les fichiers font l'objet d'une étude séparée dans la suite de ce cours, étant donné leur importance et les notions qui leur sont attachées. Nous avons réservé une place en fin de chapitre aux constantes déclarées avec type et aux variables absolues.

5.2 Type ensemble (set) En Pascal, les ensembles sont mis en oeuvre avec la même signification qu'ils possèdent en mathématique. On y retrouve d'ailleurs les mêmes opérations. Pour mieux comprendre l'utilité du type ensemble, considérons le problème suivant. Comment savoir si un caractère tapé dans un Edit par l'utilisateur est une voyelle majuscule ? Une structure sélective permet de résoudre facilement ce problème. Elle doit être placée dans l'événement OnKeyPress de l'Edit.: procedure TForm1.Edit1KeyPress(Sender: TObject; var Key: Char); begin if (Key = 'A') or (Key = 'E') or (Key = 'I') or (Key = 'O') or (Key = 'U') or (Key = 'Y') then Label1.caption := 'C''est une voyelle'; end; Ce genre de problème, consistant à déterminer si le contenu d'une variable appartient à un ensemble de valeurs données, se rencontre souvent en programmation. Voici comment il est possible de résoudre ce problème à l'aide d'un ensemble: procedure TForm1.Edit1KeyPress(Sender: TObject; var Key: Char); begin if Key in ['A','E','I','O','U','Y'] then Label1.caption := 'C''est une voyelle'; end; Delphi (J-C Armici janvier 2003 www.unvrai.com)

page 5-1

Ici, l'ensemble des voyelles est spécifié de manière explicite par une liste de constantes placée entre crochets, appelée constructeur d'ensemble. Cette forme d'écriture abrégée est bien plus élégante, plus claire et plus concise qu'une expression booléenne complexe. L'ensemble des voyelles peut, dans cet exemple, être considéré comme une constante de type ensemble. Un ensemble contient toujours des éléments du même type de base, scalaire (prédéfini ou énuméré) ou intervalle, à l'exception du type réel. Le type ensemble se déclare de la manière suivante: SYNTAXE type type_ensemble = set of type_de_base; Une variable de type type_ensemble peut contenir des éléments de type type_de_base, qui doit être un type scalaire ou intervalle, à l'exception du type réel. Le nombre maximum d'éléments d'un ensemble est limité à 256. De plus, deux ensembles sont égaux s'ils contiennent les mêmes éléments, indépendamment de leur l'ordre. La définition d'une variable de type ensemble comporte la spécification du type de base des éléments de cet ensemble. Seule une affectation permet de placer des valeurs dans une variable de ce type. L'erreur consistant à croire que la déclaration d'une telle variable suffit à lui attribuer des éléments se rencontre fréquemment. L'affectation d'éléments à un ensemble s'effectue à l'aide d'un constructeur d'ensemble qui n'est autre qu'une liste d'éléments du type de base, délimitée par des crochets. La notation d'intervalle est possible lorsque plusieurs éléments sont consécutifs. Le fragment de programme qui suit montre quelques exemples de déclaration et d'utilisation de variables de type ensemble: type alphabet couleurs teinte bizarre

= = = =

set of 'A'..'Z'; (rouge, vert, bleu, jaune, blanc); set of couleurs; set of 0..255;

var lettre, voyelle : alphabet; c : char; lumiere : teinte; clair : set of jaune..blanc; magique : bizarre; ... begin lettre := ['A'..'Z']; voyelle := ['A','E','I','O','U','Y']; lumiere := [blanc]; clair := [jaune, blanc]; magique := [0..8,12,19,22..38,99]; ... if c in voyelle then Edit1.Text := 'Voyelle'; end; ... Le langage Pascal dispose d'opérateurs agissant sur les ensembles, de manière analogue aux opérateurs de la théorie des ensembles en mathématique. On trouve, d'un côté, les opérateurs ensemblistes proprement dits (union, intersection et différence) et, de l'autre, des opérateurs relationnels (appartenance, égalité, altérité, inclusion). Comme pour les nombres, ces opérateurs ont un degré de priorité et, en cas de même degré de priorité, l'évaluation des expressions se fait de gauche à droite. Mais voyons comment s'expriment ces opérateurs en Pascal. Les variables utilisées sont déclarées comme suit: var nombres, resultat, a, b : set of 0..10; Delphi (J-C Armici janvier 2003 www.unvrai.com)

page 5-2

reponse : char; daccord : boolean; intersection * nombres := [1..10]; resultat := nombres * [2,4..6];

// resultat aura la valeur [2,4,5,6]

a := [1,2,4]; b := [3,5];

// [ ] représente l'ensemble vide. Label1.Caption := 'a et b sont disjoints';

if a * b = [] then

union + resultat := a + b + [6,7,8,9];

// resultat aura la valeur // [1,2,3,4,5,6,7,8,9]

différence resultat := nombres - [1,3..7];

// resultat aura la valeur [2,8,9]

égalité = if (nombres = [1..10]) then resultat := nombres - [5..10]; altérité if (nombres - [5..10]) [] then Label1.Caption := 'Correct'; inclusion 0 then begin Delphi (J-C Armici janvier 2003 www.unvrai.com)

page 4-19

messagedlg ('Erreur d''ouverture du fichier source', mtError, [mbOK],0); exit; end; AssignFile (desti, dest.text); {$I-} Rewrite (desti, 1); { taille d'un record = 1 } {$I+} if ioresult > 0 then begin messagedlg ('Erreur d''ouverture du fichier destination', mtError, [mbOK],0); CloseFile (src); exit; end; repeat BlockRead (src, Buf, SizeOf (Buf), Nblu); BlockWrite (desti, Buf, Nblu, Nbecrit); until (Nblu = 0) or (Nbecrit Nblu); CloseFile (src); CloseFile (desti); end; Avec Delphi, il est toutefois préférable de traiter les erreur d'accès aux fichiers à l'aide des exceptions (voir plus loin). De plus, l'utilisation de dialogues standard d'ouverture et de sauvegarde de fichiers permet généralement d'éviter les erreurs.

Delphi (J-C Armici janvier 2003 www.unvrai.com)

page 4-20