Extension et recherches élémentaires▲
Introduction▲
Les précédents chapitres nous ont permis de découvrir une base de vocabulaire consistante, et de créer avec Lazarus(29) les outils permettant de la parcourir et de l'enrichir.
Cet enrichissement est possible soit en ajoutant des mots, soit en établissant des liens entre eux, de façon à permettre de chercher des associations d'idées potentielles.
Dans le présent chapitre, nous allons introduire un complément substantiel à notre vocabulaire, clarifier l'interface graphique, et aborder le domaine des recherches en créant un masque de saisie.
Environnement▲
Chapitre 9…
Créons un répertoire Lex9 et recopions dans ce nouveau répertoire tous les fichiers du répertoire Lex8 utilisé précédemment. Pour éviter toute difficulté ultérieure, suivez la check-list :
- ouvrir pLex8.lpi dans Lex9 avec Lazarus ;
- enregistrer uLex8.pas sous le nom de uLex9.pas ;
- accepter la suppression des références à uLex8.pas ;
- enregistrer pLex8.pas sous le nom de pLex9.pas ;
- renommer la fenêtre Lex8 en Lex9 ;
- dans le répertoire Lex9, supprimer les anciens fichiers contenant la mention Lex8 ainsi que le fichier fondamental liste.de.mots.francais.frgut.txt désormais inutile ;
- dans la procédure MAJBalayage, remplacer Lex8 par Lex9.
Nous retrouvons le projet dans l'état où nous l'avions laissé, et les modifications que nous allons effectuer n'affecteront pas l'étape précédente consultable dans le répertoire Lex8.
Extension du vocabulaire▲
Nous savons maintenant créer des liens (fichier fichLiens.bin, au format binaire) entre les différentes entrées de la liste principale (fichier liMots.txt, au format texte), et le précédent chapitre nous a montré comment modifier cette liste.
L'objectif est évidemment d'étendre les listes de mots et de liens de façon à constituer une base consistante pour nos recherches à venir.
Ce travail a déjà commencé, et les fichiers ci-jointsTéléchargez les deux fichiers vous éviteront une saisie fastidieuse : il a été réalisé à partir de définitions de mots croisés.
Pour les utiliser, il suffit de les enregistrer et de les placer dans le répertoire Lex9 que vous venez de créer, en remplacement des deux fichiers existants.
Un clic sur le petit triangle vert pour lancer l'exécution.
L'existence de deux fichiers distincts pour stocker nos données peut sembler inutilement compliquée. Nous verrons ultérieurement comment les fusionner.
Nombre de mots▲
Le nombre de mots passe de 336Â 531 Ã 337Â 254(30)Â : la variation est donc modeste, ce qui est naturel compte tenu de la taille impressionnante de la liste d'origine.
Attention à l'ordre des mots : ce n'est pas celui du dictionnaire, en raison des options adoptées, notamment au sujet des majuscules, des lettres accentuées et des caractères spéciaux.
Les apports proviennent essentiellement de noms propres, mais il y a également quelques mots courants qui, étonnamment, sont absents de la liste d'origine, comme « tansad », « addendum », « ilet », « dyne », « lieder », « cousinage », « athénien », « mât », « pet », « abracadabra », « crédit », « aquifère »…
La nouvelle liste ne pourra que s'allonger avec l'évolution de la langue et… les ruses des cruciverbistes : qui ne connaît les verbes « stabiloter » ou « podcaster » alors qu'ils ne figurent encore dans aucun dictionnaire ?
Et les néologismes ne font pas peur à nos auteurs !
Nombre de liens▲
Le nombre de mots liés passe pour sa part de quelques dizaines à plus de 5 100 : c'est le résultat de la saisie de définitions, principalement dues à Michel Laclos, auteur récemment disparu à qui nous rendons particulièrement hommage en raison de la qualité de son humour et… du nombre de grilles qu'il a su créer.
Dans la pratique, un même mot de la liste principale peut se retrouver dans des définitions différentes, ce qui fait que le nombre de liens réels est très supérieur au nombre affiché.
Pour l'évaluer, il suffit d'ajouter une variable dans la procédure lireLiens, de l'incrémenter après chaque fonction Read, et d'afficher sa valeur : les plus curieux pourront vérifier que le nombre de liens dépasse déjà , dans l'état actuel des saisies, les 17 000 ! Le code peut s'écrire ainsi :
procedure lireLiens;
var
j, m : integer;
fLiens : file of integer;
nombreL : integer;
begin
nLiens := 0; //nombre de mots liés
nombreL := 0; //nombre de liens
AssignFile(fLiens, 'fichLiens.bin');
{$I-}
Reset(fLiens);
{$I+}
if IOResult<>0 then
ShowMessage('Fichier liens inexistant')
else
while not EOF(fLiens) do
begin
SetLength(Liens, nLiens+1); //taille du tableau principal
Read(fLiens, m);
SetLength(Liens[nLiens], m); //taille du tableau secondaire
for j := 0 to m-1 do
Read(fLiens, Liens[nLiens, j]); //remplissage du tableau secondaire
inc(nLiens); //nombre de mots liés
inc(nombreL, m); //nombre de liens
end;
CloseFile(fLiens);
ShowMessage('Nombre de liens : '+IntToStr(nombreL));
end;
end;Une fois la vérification faite, il sera préférable de supprimer toutes les modifications apportées à la procédure.
La base de données sera d'autant plus intéressante qu'elle aura été enrichie : une collaboration permettrait une évolution rapide. En attendant, un thésaurus personnel n'est pas non plus à négliger.
Interface de saisie▲
Pour l'instant, les mots nouveaux sont saisis dans l'onglet Mots, et les liens dans l'onglet Liens. Cette bascule ne présente pas l'ergonomie souhaitable pour une saisie confortable : le lien introduit peut se révéler être un mot nouveau, ce qui conduit à le saisir une seconde fois dans l'onglet Mots.
Nous allons introduire un nouveau bouton dans l'onglet Liens : sa fonction sera de remplacer le mot titre par le mot saisi ; son libellé sera naturellement une flèche vers le haut. Pour sa part, le libellé « Lier » de l'ancien bouton sera remplacé par une flèche vers la droite, qui exprime bien que le mot saisi doit basculer dans l'espace réservé aux liens.
Les deux boutons seront déplacés et réduits pour tenir sur la même ligne que le TEdit, et donner un aspect à la fois simple et fonctionnel : voir ci-dessus.
L'introduction de signes spéciaux dans les boutons n'est pas simple.
Nous proposons la méthode suivante :
- Dans l'éditeur de source, placer le curseur en fin de ligne (n'importe laquelle) ;
- Dans le menu Lazarus, cliquer sur le mot Éditer, et choisir la dernière option : insérer depuis la table de caractères (Shift+Ctrl+M) ;
- Dans l'onglet Unicode, sélectionner la police Arrows ;
- Cliquer une fois sur la flèche choisie, par exemple la flèche à droite, qui porte le code UTF8 $E2$87$A8 ; flèche vers le haut code UTF8 $E2$87$AA ;
- Ce caractère est inséré dans l'éditeur de source : le couper puis le coller dans la propriété Caption du bouton…
- De même pour le second bouton (flèche vers le haut).
En fermant la table de caractères, vérifiez quand même que l'éditeur de source ne contienne pas de caractère parasite…
Pour améliorer la présentation, nous avons ajusté quelques propriétés :
- Color portée à $00E3FED8 pour Label et ListBox ;
- BorderStyle portée à bsNone pour la ListBox ;
- Font/Size portée à 24 pour les boutons ;
- ShowHint portée à True pour les deux boutons ;
- Hint renseignée à « vers Titre » pour l'un, « vers Liens » pour l'autre, de façon à informer l'utilisateur quand le curseur survolera l'un ou l'autre des composants.
Notre onglet, après compilation, se présente maintenant ainsi :
Ancien bouton▲
Le libellé « Lier » de l'ancien bouton est remplacé maintenant par une flèche à droite.
Sa procédure OnClick est légèrement modifiée :
procedure TForm1.Button3Click(Sender: TObject);
var iLien, k : integer;
motNouv : string;
begin
if Edit3.Caption > #47 then
begin
motNouv := UTF8ToAnsi(Edit3.Caption);
iLien := listeMots.IndexOf(motNouv);
if iLien<0 then iLien := fAnnonceMotNouveau(motNouv);
if (iLien>=0) and (iLien<>iMot) and not fDoublon(iMot, iLien) then
begin
Lier(iMot, iLien);
Lier(iLien, iMot);
end;
AffLiens;
end
else ShowMessage('Entrez le mot');
Edit3.Clear;
end;Vous notez pour commencer un premier contrôle sur le mot saisi : le premier caractère doit être un chiffre ou une lettre(31).
Si le mot est nouveau, la position future du mot est définie directement par la fonction fAnnonceMotNouveau(32), qui remplacera avec plus d'efficacité la procédure existante AnnonceMotNouveau :
function TForm1.fAnnonceMotNouveau(motNouv: string): integer;
var i, indexMotNouv : integer;
Rep : string;
begin
if listeMots.IndexOf(motNouv)<0 then
begin
indexMotNouv := fMotNouv(motNouv);
Rep := 'Ajouter le mot '+ AnsiToUTF8(motNouv) +'Â ? ';
if Length(tabApprox)>0 then //affichage des mots proches éventuels
begin
Rep := Rep +#13#10 + 'Mots existants : ';
For i:=0 to Length(tabApprox) - 1 do
Rep := Rep + ANSIToUTF8(listeMots[tabApprox[i]])+' /';
end;
if MessageDlg ('Mot nouveau', Rep, mtConfirmation,
[mbYes, mbNo],0) = mrYes
then
begin
AjoutMot(motNouv, indexMotNouv);
fAnnonceMotNouveau := indexMotNouv;
end
else fAnnonceMotNouveau := -1;
end;
end;La création du lien est maintenant précédée de deux contrôles supplémentaires :
- le lien ne doit pas boucler un mot sur lui-même ;
- le lien ne peut être ajouté s'il existe déjà .
Ce deuxième contrôle est réalisé par la fonction fDoublon :
function TForm1.fDoublon(iMot, iLien: integer): boolean;
var i, j : integer;
begin
//vérifie que le lien n'existe pas déjÃ
//cherche le tableau de liens correspondant à iMot
fDoublon := False;
j := chercheTab(iMot);
if j>=0 then
begin
i := Length(Liens[j])-1;
while (i>0) and (iLien<Liens[j, i]) do dec(i);
if iLien = Liens[j, i] then fDoublon := True;
if fDoublon then ShowMessage('Doublon refusé');
end;
end;Remarquez que la liste des liens est triée : il est inutile de la parcourir dans sa totalité.
La suppression de la procédure AnnonceMotNouveau conduit à remanier les opérations déclenchées par un clic sur le bouton Ajouter de l'onglet Mots :
procedure TForm1.Button7Click(Sender: TObject);
var i : integer;
motNouv : string;
begin
motNouv := UTF8ToAnsi(Edit4.Caption);
i := listeMots.IndexOf(motNouv);
if (i >= 0) then ShowMessage('Mot existant')
else
begin
i := fAnnonceMotNouveau(motNouv);
if i>=0 then iMot := i;
Edit4.Clear;
end;
end;De cette façon, les filtrages de sécurité s'appliqueront aussi quand l'utilisateur passera par cet onglet.
Nouveau bouton▲
Le nouveau bouton, dont le libellé (propriété Caption) est une flèche vers le haut, a pour fonction de remplacer le mot titre par le mot qui vient d'être saisi. Après filtrage, deux cas de figure se présentent : soit c'est un mot nouveau, soit c'est un mot existant. Cliquez deux fois sur le nouveau bouton et, dans la procédure OnClick qui est ainsi créée, nous complétons le code comme suit :
procedure TForm1.Button10Click(Sender: TObject);
var iLien : integer;
motNouv : string;
begin
if Edit3.Caption > #47 then
begin
motNouv := UTF8ToAnsi(Edit3.Caption);
iLien := listeMots.IndexOf(motNouv);
if iLien<0 then iLien := fAnnonceMotNouveau(motNouv);
if (iLien>=0) and (iLien<>iMot) and not fDoublon(iMot, iLien) then
begin
Label4.Caption:= Edit3.Caption;
iMot := iLien;
end;
AffLiens;
end
else ShowMessage('Entrez le mot');
Edit3.Clear;
end;Un clic sur le petit triangle vert de Lazarus, pour lancer l'exécution. Testez avec un mot nouveau, liez un mot nouveau… Notre moteur est maintenant bien rôdé et son interface plus simple.
Après avoir vérifié que tout se passe bien, vous pouvez nettoyer le code en éliminant la procédure AnnonceMotNouveau, devenue inutile(33).
Recherche▲
Chercher un mot peut s'envisager de deux façons élémentaires :
- à l'aide d'un masque, on précise le nombre de lettres et les lettres connues avec leur position ;
- dans une fenêtre, on indique le ou les mots apparentés au mot cherché et le programme doit indiquer la ou les solutions les plus logiques.
Nous avons déjà un onglet Recherche : il a eu son rôle au début de notre projet, mais semble nettement obsolète.
Pour orienter l'utilisateur, nous adopterons maintenant deux onglets :
- un onglet Masque, qui remplacera l'onglet Recherche existant ;
- un onglet Logique, dans lequel seront lancées les opérations de tri sur les liens.
Par ailleurs, revenons à notre bouton Arrêt qui provoque l'enregistrement des données au moment où on quitte le logiciel. Nous créons un onglet supplémentaire, appelé Fichiers, qui recevra ce bouton, et sera disponible pour des fonctions annexes.
Onglets▲
Pour accéder aux fonctions d'édition des onglets de notre interface graphique, nous effectuons un clic droit sur la ligne en grisé qui affiche les onglets existants. Le menu contextuel donne le choix entre six fonctions de page : ajouter, insérer, supprimer, déplacer (à droite ou à gauche) et afficher.
- Commençons par la fonction Ajouter : un nouvel onglet est créé en dernière position et nous le renommons (dans l'onglet propriétés/caption de l'inspecteur d'objets) en Fichier.
- Dans l'onglet Recherche, nous coupons le bouton Arrêt et nous le copions dans l'onglet Fichier. Nous renommons l'onglet Recherche en Masque.
- Nouveau clic droit dans la ligne supérieure, et cette fois nous sélectionnons la fonction Insérer : un nouvel onglet est créé et nous le renommons Logique ; nous le déplaçons pour qu'il soit en seconde position (TabIndex porté à 1).
- Nous pouvons supprimer l'onglet Info, devenu inutile. D'abord, enlevons tous les composants, un par un. Ensuite, supprimons les procédures Button4Click et Button5Click attachées aux boutons, ainsi que leurs déclarations (avant Implémentation). Dans la procédure MAJBalayage, nous supprimons également la ligne qui fait référence à Label3.Caption. L'onglet est maintenant propre, et nous pouvons le supprimer sans problème.
- Les boutons qui restent en haut et à droite de notre interface graphique peuvent provoquer des résultats inattendus, et il est donc préférable de les supprimer. Pour cela, nous sélectionnons, dans les propriétés de Form1 la ligne BorderIcons et portons les propriétés à False :
Pour quitter le programme, il faudra passer par l'onglet Fichier et cliquer sur le bouton Arrêt pour assurer l'enregistrement des saisies.
Dans le présent chapitre, nous reconstruirons l'onglet Masque. La recherche logique, plus complexe, sera traitée dans le chapitre suivant.
Masque▲
Nous allons aborder successivement le principe de la saisie, sa mise en œuvre, et le traitement des cas particuliers.
Principe▲
Dans une grille de mots croisés, nous disposons du nombre de cases occupées par le mot et, éventuellement, d'une ou plusieurs lettres. Pour saisir ces données, nous allons donc entrer ces lettres connues, séparées par autant de signes « $ » que de cases vides. Par exemple, une entrée « a$b$e » indiquera une recherche d'un mot de cinq lettres, dont la première lettre est un a, la troisième un b, etc.
Interface graphique▲
Dans l'onglet Masque, nous consacrerons le composant TEdit à la saisie du masque, le Label à l'affichage du nombre de lettres, la ListBox à la présentation des résultats.
Les composants UpDown et Memo peuvent être supprimés, mais il faudra aussi supprimer la procédure UpDown1Click et sa déclaration.
Les procédures FormCreate et ListBox3Click doivent, pour leur part, être modifiées pour leurs références à Memo1 et Label1 :
procedure TForm1.FormCreate(Sender: TObject);
begin
listeMots := TStringList.Create;
listeMots.CaseSensitive:=True;
LireFichier(listeMots);
nMots := listeMots.Count;
lireLiens;
iMot := 0;
Edit3.Clear;
Edit2.Clear;
Label1.Caption := 'saisir les lettres séparées par le signe $';
MAJBalayage;
cAcc := UTF8ToAnsi('à âäéèêëïîôùûüÿç-'' ');
//regroupe tous les caractères à remplacer
sAcc := 'aaaeeeeiiouuuyc';
//regroupe tous les caractères de substitution
end;
procedure TForm1.ListBox3Click(Sender: TObject);
begin
if ListBox3.ItemIndex>=0 then
begin
iMot := tabApprox[ListBox3.ItemIndex];
Edit2.Clear;
ListBox3.Clear;
MAJBalayage;
end;
end;Enfin, la procédure MAJAffichage peut être complètement supprimée(34).
Côté interface graphique, l'onglet Masque est nettement épuré :
Code▲
Un clic va déclencher la recherche qui sera réalisée par la procédure Masque. La procédure onClick du bouton doit être remaniée(35) :
procedure TForm1.Button2Click(Sender: TObject);
begin
//Recherche(UTF8ToAnsi(Edit2.Caption));
Masque(SansAccent(UTF8ToAnsi(Edit2.Caption)));
end;La recherche commence par noter la longueur du mot, puis balaye les lettres une par une. En cas de divergence avec le mot saisi, le mot en cours est abandonné. Si la recherche est positive, le mot est affiché dans la listbox.
procedure TForm1.Masque(rechMot: string);
var i, j, k : integer;
motCour : string;
begin
i := 0;
k := Length(rechMot);
Label1.Caption:= ('Mot de ' + IntToStr(k)+ ' lettres' );
repeat
motCour := SansAccent(listeMots[i]);
if Length(motCour) = k then //balayage du mot pour comparaison des lettres
begin
j := 1;
while (j<k) and ((rechMot[j]='$') or (rechMot[j] = motCour[j])) do inc(j);
if j=k then ListBox3.Items.Append(AnsiToUTF8(listeMots[i]));
end;
inc(i);
until (i=nMots) or ((rechMot[1]='$') and (rechMot[1] > motCour[1]));
//balayage tant que la première lettre est inférieure à celle du masque
end;Faisons un essai avec le masque « a$b$e ».
Nous trouvons cinq occurrences pouvant convenir à une grille de mots croisés :
Déjà , on peut apprécier la différence avec une recherche sur un dictionnaire habituel !
Que se passe-t-il si l'utilisateur clique sur l'un des mots présentés ? Il convient de modifier la procédure onClick de ListBox3 :
procedure TForm1.ListBox3Click(Sender: TObject);
begin
if ListBox3.ItemIndex>=0 then
begin
iMot := listeMots.IndexOf(ListBox3.Items[ListBox3.ItemIndex]);
Label1.Caption:= listeMots[iMot];
end;
end;Un clic fait apparaître le mot sélectionné en titre, et l'interface se met à jour.
Conclusion▲
Notre liste de mots trouve maintenant une flexibilité appréciable : les définitions de mots croisés peuvent être enregistrées aisément, et les recherches sont facilitées par la mise en place d'un masque.
Mais est-il possible d'exploiter la logique ou l'humour de ces définitions ? C'est ce que nous tenterons de faire dans le prochain chapitre.
Outre la logique, il nous reste à unifier les fichiers mots/liens, à améliorer l'ergonomie, à enrichir nos sources de mots et de définitions…
Et tant d'autres thèmes que nos lecteurs vont peut-être réclamer…
Beaucoup de divorces sont nés d'un malentendu.
Beaucoup de mariages aussi. »
Tristan Bernard
Le codeTéléchargez le code complet… L'unité uLex9 se présente maintenant comme suit :
unit ulex9;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, FileUtil, Forms, Controls,
Graphics, Dialogs, StdCtrls, ComCtrls, uDisque;
type
{ TForm1 }
TForm1 = class(TForm)
Button1: TButton;
Button10: TButton;
Button2: TButton;
Button3: TButton;
Button6: TButton;
Button7: TButton;
Button8: TButton;
Button9: TButton;
CheckBox1: TCheckBox;
Edit2: TEdit;
Edit3: TEdit;
Edit4: TEdit;
Label4: TLabel;
Label5: TLabel;
Label6: TLabel;
ListBox1: TListBox;
ListBox2: TListBox;
ListBox3: TListBox;
ListBox4: TListBox;
RadioButton1: TRadioButton;
RadioButton2: TRadioButton;
RadioButton3: TRadioButton;
RadioButton4: TRadioButton;
TabSheet3: TTabSheet;
TabSheet4: TTabSheet;
TabSheet5: TTabSheet;
TabSheet6: TTabSheet;
TabSheet7: TTabSheet;
Zoom: TGroupBox;
Label1: TLabel;
Label2: TLabel;
AffListe: TListBox;
PageControl1: TPageControl;
Page1: TTabSheet;
TabSheet1: TTabSheet;
TrackBar1: TTrackBar;
UpDown2: TUpDown;
procedure AffListeClick(Sender: TObject);
procedure Button10Click(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure Button6Click(Sender: TObject);
procedure Button7Click(Sender: TObject);
procedure Button8Click(Sender: TObject);
procedure Button9Click(Sender: TObject);
procedure CheckBox1Change(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure ListBox1Click(Sender: TObject);
procedure ListBox2Click(Sender: TObject);
procedure ListBox3Click(Sender: TObject);
procedure ListBox4Click(Sender: TObject);
procedure Recherche(rechMot: string);
procedure TabSheet1Show(Sender: TObject);
procedure TabSheet3Show(Sender: TObject);
procedure TabSheet4Show(Sender: TObject);
procedure TabSheet5Show(Sender: TObject);
procedure TrackBar1MouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
//procedure MAJAffichage;
procedure MAJBalayage;
procedure UpDown2Click(Sender: TObject; Button: TUDBtnType);
procedure ZoomMouseLeave(Sender: TObject);
procedure MAJInfo;
function chercheTab(iMot : integer) : integer;
procedure Lier(iMot, iLien : integer);
procedure AffLiens;
//function TriSec(k : integer) : boolean;
//function TriPPal : boolean;
procedure PlaceLien(k : integer);
function PlaceTab(k : integer) : integer;
function SansAccent(rMot : string) : string;
procedure listeApprox(rechMot : string);
procedure MAJSupp;
procedure SuppLien(iMot, iLien : integer);
function fMotNouv(rechMot : string) : integer;
function fAnnonceMotNouveau(motNouv : string) : integer;
procedure AjoutMot(motNouv : string; indexMotNouv : integer);
procedure AjoutMotSecur(motNouv : string ; indexMotNouv : integer);
procedure SupMot(motCour : string; indexMotCour : integer);
function fDoublon(iMot, iLien: integer): boolean;
procedure Masque(rechMot: string);
private
{ private declarations }
public
{ public declarations }
end;
const delta=5;
var
Form1: TForm1;
listeMots, listeInfo : TstringList;
iMot, nMots, nLiens, iLien : integer;
AffListe : TListBox;
Liens : Array of Array of integer;
tabApprox : Array of integer; //index des mots approchants
sAcc, cAcc : string;
implementation
{$R *.lfm}
{ TForm1 }
procedure TForm1.Button1Click(Sender: TObject);
begin
regLiens;
regFichier(listeMots);
listeMots.Free;
Application.Terminate;
end;
procedure TForm1.AffListeClick(Sender: TObject);
begin
iMot := (iMot -delta + AffListe.ItemIndex + nMots) mod nMots;
MAJBalayage;
end;
procedure TForm1.Button10Click(Sender: TObject);
var iLien : integer;
motNouv : string;
begin
if Edit3.Caption > #47 then
begin
motNouv := UTF8ToAnsi(Edit3.Caption);
iLien := listeMots.IndexOf(motNouv);
if iLien<0 then iLien := fAnnonceMotNouveau(motNouv);
if (iLien>=0) and (iLien<>iMot) and not fDoublon(iMot, iLien) then
begin
Label4.Caption:= Edit3.Caption;
iMot := iLien;
end;
AffLiens;
end
else ShowMessage('Entrez le mot');
Edit3.Clear;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
//Recherche(UTF8ToAnsi(Edit2.Caption));
ListBox3.Clear;
Masque(SansAccent(UTF8ToAnsi(Edit2.Caption)));
end;
function TForm1.fMotNouv(rechMot: string): integer;
var i, k : integer;
referMot : string;
begin
SetLength(tabApprox, 0); //mise à zéro du tableau
referMot := SansAccent(rechMot);
//point de démarrage de la recherche : première lettre du mot
i := 0;
k := 0;
while SansAccent(listeMots[i][1]) < referMot[1] do inc(i);
//début de la recherche
repeat
//k index futur du mot nouveau
if (k=0) and (SansAccent(listeMots[i]) > referMot) then k := i
else if SansAccent(listeMots[i]) = referMot then
begin
//enregistrement des index pour réutilisation
SetLength(tabApprox, Length(tabApprox)+1);
tabApprox[Length(tabApprox)-1] := i;
end;
inc(i)
until (i>nMots-1) or (SansAccent(listeMots[i][1]) > referMot[1]);
if k=0 then fMotNouv := nMots else fMotNouv := k;
end;
//==========================
function TForm1.fAnnonceMotNouveau(motNouv: string): integer;
var i, indexMotNouv : integer;
Rep : string;
begin
if listeMots.IndexOf(motNouv)<0 then
begin
indexMotNouv := fMotNouv(motNouv);
Rep := 'Ajouter le mot '+ AnsiToUTF8(motNouv) +'Â ? ';
if Length(tabApprox)>0 then //affichage des mots proches éventuels
begin
Rep := Rep +#13#10 + 'Mots existants : ';
For i:=0 to Length(tabApprox) - 1 do
Rep := Rep + ANSIToUTF8(listeMots[tabApprox[i]])+' /';
end;
if MessageDlg ('Mot nouveau', Rep, mtConfirmation,
[mbYes, mbNo],0) = mrYes
then
begin
AjoutMot(motNouv, indexMotNouv);
fAnnonceMotNouveau := indexMotNouv;
end
else fAnnonceMotNouveau := -1;
end;
end;
procedure TForm1.AjoutMot(motNouv: string; indexMotNouv: integer);
var nouvLiens : array of array of integer;
i, j : integer;
begin
nouvLiens := Liens;
for i:=0 to Length(Liens)-1 do
for j:=0 to Length(Liens[i])-1 do
if Liens[i, j] >= indexMotNouv then inc(Liens[i, j]);
if iMot >= indexMotNouv then inc(iMot); //mise à jour effectuée
SetLength(nouvLiens, 0);
listeMots.Insert(indexMotNouv, motNouv); //modification de la liste principale
inc(nMots);
end;
//===========doublons=======================
function TForm1.fDoublon(iMot, iLien: integer): boolean;
var i, j : integer;
begin
//vérifie que le lien n'existe pas déjÃ
//cherche le tableau de liens correspondant à iMot
fDoublon := False;
j := chercheTab(iMot);
if j>=0 then
begin
i := Length(Liens[j])-1;
while (i>0) and (iLien<Liens[j, i]) do dec(i);
if iLien = Liens[j, i] then fDoublon := True;
if fDoublon then ShowMessage('Doublon refusé');
end;
end;
procedure TForm1.Masque(rechMot: string);
var i, j, k : integer;
motCour : string;
begin
i := 0;
k := Length(rechMot);
Label1.Caption:= ('Mot de ' + IntToStr(k)+ ' lettres' );
ListBox3.Clear;
repeat
motCour := SansAccent(listeMots[i]);
if Length(motCour) = k then //balayage du mot pour comparaison des lettres
begin
j := 1;
while (j<=k) and ((rechMot[j]='$') or (rechMot[j] = motCour[j])) do inc(j);
if j>k then ListBox3.Items.Append(AnsiToUTF8(listeMots[i]));
end;
inc(i);
until (i=nMots) or ((rechMot[1]='$') and (rechMot[1] > motCour[1]));
//balayage tant que la première lettre est inférieure à celle du masque
if ListBox3.Count=0 then Label1.Caption:= ('échec')
else Label1.Caption:= (IntToStr(ListBox3.Count)+
' mots trouvés');
end;
procedure TForm1.AjoutMotSecur(motNouv: string; indexMotNouv: integer);
var nouvLiens : array of array of integer;
i, j : integer;
begin
SetLength(nouvLiens, Length(Liens));
for i:=0 to Length(Liens)-1 do
begin
SetLength(nouvLiens[i], Length(Liens[i]));
for j:=0 to Length(Liens[i])-1 do
if Liens[i, j] >= indexMotNouv then nouvLiens[i, j] := Liens[i, j] + 1
else nouvLiens[i, j] := Liens[i, j];
end;
Liens := nouvLiens; //mise à jour effectuée
//SetLength(nouvLiens, 0); //suppression de l'objet en mémoire
if iMot >= indexMotNouv then inc(iMot);
listeMots.Insert(indexMotNouv, motNouv); //modification de la liste principale
inc(nMots);
end;
//==============================
procedure TForm1.SupMot(motCour: string; indexMotCour: integer);
var nouvLiens : array of array of integer;
i, j : integer;
begin
SetLength(nouvLiens, Length(Liens));
for i:=0 to Length(nouvLiens)-1 do
begin
SetLength(nouvLiens[i], Length(Liens[i]));
for j:=0 to Length(nouvLiens[i])-1 do
if Liens[i, j] >= indexMotCour then nouvLiens[i, j] := Liens[i, j] - 1
else nouvLiens[i, j] := Liens[i, j];
end;
if iMot >= indexMotCour then dec(iMot);
Liens := nouvLiens;
//mise à jour effectuée
listeMots.Delete(indexMotCour); //modification de la liste principale
dec(nMots);
end;
procedure TForm1.Button3Click(Sender: TObject);
var iLien, k : integer;
motNouv : string;
begin
if Edit3.Caption > #47 then
begin
motNouv := UTF8ToAnsi(Edit3.Caption);
iLien := listeMots.IndexOf(motNouv);
if iLien<0 then iLien := fAnnonceMotNouveau(motNouv);
if (iLien>=0) and (iLien<>iMot) and not fDoublon(iMot, iLien) then
begin
Lier(iMot, iLien);
Lier(iLien, iMot);
end;
AffLiens;
end
else ShowMessage('Entrez le mot');
Edit3.Clear;
end;
procedure TForm1.Button6Click(Sender: TObject);
begin
SuppLien(iMot, iLien);
SuppLien(iLien, iMot);
end;
//=================
procedure TForm1.Button7Click(Sender: TObject);
var i : integer;
motNouv : string;
begin
motNouv := UTF8ToAnsi(Edit4.Caption);
i := listeMots.IndexOf(motNouv);
if (i >= 0) then ShowMessage('Mot existant')
else
begin
i := fAnnonceMotNouveau(motNouv);
if i>=0 then iMot := i;
Edit4.Clear;
end;
end;
procedure TForm1.Button8Click(Sender: TObject);
var indexMotCour : integer;
motCour, Rep : string;
begin
//le mot peut être supprimé si aucun lien ne lui est affecté
motCour := UTF8ToAnsi(Edit4.Caption);
indexMotCour := listeMots.IndexOf(motCour);
if (indexMotCour<0) then ShowMessage ('Supprimé')
else if (chercheTab(indexMotCour)<0) then
begin
Rep := 'Supprimer le mot '+ Edit4.Caption +' ? ';
if MessageDlg ('Suppression', Rep, mtConfirmation,
[mbYes, mbNo],0) = mrYes
then SupMot(motCour, indexMotCour);
MAJBalayage;
Edit4.Clear;
end
else ShowMessage ('Supprimez les liens avant de supprimer le mot');
end;
procedure TForm1.Button9Click(Sender: TObject);
begin
ShowMessage('Supprimez le mot, puis ajoutez le nouveau mot');
end;
procedure TForm1.CheckBox1Change(Sender: TObject);
begin
if CheckBox1.Checked then CheckBox1.Caption := 'Avec filtre'
else CheckBox1.Caption := 'Sans filtre';
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
listeMots := TStringList.Create;
listeMots.CaseSensitive:=True;
LireFichier(listeMots);
nMots := listeMots.Count;
lireLiens;
iMot := 0;
Edit3.Clear;
Edit2.Clear;
Label1.Caption := 'saisir les lettres séparées par le signe $';
MAJBalayage;
cAcc := UTF8ToAnsi('à âäéèêëïîôùûüÿç-'' ');
//regroupe tous les caractères à remplacer
sAcc := 'aaaeeeeiiouuuyc';
//regroupe tous les caractères de substitution
end;
procedure TForm1.ListBox1Click(Sender: TObject);
begin
If ListBox1.ItemIndex >= 0 then
begin
iMot := Liens[chercheTab(iMot), ListBox1.ItemIndex+1];
Label4.Caption := AnsiToUTF8(listeMots[iMot]);
AffLiens;
end;
end;
procedure TForm1.ListBox2Click(Sender: TObject); //couleur $00C1FFDC
begin
If ListBox2.ItemIndex >= 0 then
begin
iMot := Liens[chercheTab(iMot), ListBox2.ItemIndex+1];
MAJBalayage;
end;
end;
procedure TForm1.ListBox3Click(Sender: TObject);
begin
if ListBox3.ItemIndex>=0 then
begin
iMot := listeMots.IndexOf(ListBox3.Items[ListBox3.ItemIndex]);
Label1.Caption:= listeMots[iMot];
end;
end;
procedure TForm1.ListBox4Click(Sender: TObject);
begin
if ListBox4.ItemIndex >= 0 then
begin
iLien := Liens[chercheTab(iMot), ListBox4.ItemIndex+1];
Label6.Caption:= 'Supprimer le lien entre '+
AnsiToUTF8(listeMots[iMot]) + ' et '+
AnsiToUTF8(listeMots[iLien])+ ' ?';
Button6.Enabled := True;
end;
end;
procedure TForm1.MAJBalayage;
var i : integer;
begin
Label2.Caption:=AnsiToUTF8(listeMots[iMot]);
Label4.Caption:=Label2.Caption;
TrackBar1.Position:= Round(iMot*1000/nMots);
Edit2.Clear;
AffListe.Clear;
for i := 0 to 10 do
AffListe.Items.Add(AnsiToUTF8(listeMots[(iMot-5 + i + nMots) mod nMots]));
AffListe.Selected[5] := True;
AffLiens;
Caption := 'Lex9 '+IntToStr(nMots)+ ' mots dont '+IntToStr(nLiens) + ' liés';
MAJSupp;
end;
procedure TForm1.UpDown2Click(Sender: TObject; Button: TUDBtnType);
begin
if CheckBox1.Checked then
repeat
inc(iMot);
until (chercheTab(iMot)>=0) or (iMot=nMots)
else
if Button=btNext then Inc(iMot, UpDown2.Increment)
else Dec(iMot, UpDown2.Increment);
iMot := (iMot + nMots) mod (nMots);
MAJBalayage;
end;
procedure TForm1.ZoomMouseLeave(Sender: TObject);
begin
if RadioButton1.Checked then UpDown2.Increment := 1
else if RadioButton2.Checked then UpDown2.Increment := 10
else if RadioButton3.Checked then UpDown2.Increment := 100
else if RadioButton4.Checked then UpDown2.Increment := 1000;
end;
procedure TForm1.MAJInfo;
begin
end;
function TForm1.chercheTab(iMot: integer): integer;
var i : integer;
begin
chercheTab := -1;
i := 0;
while (i<Length(Liens)) and (Liens[i, 0]<>iMot)do inc(i);
if i<Length(Liens) then chercheTab := i;
end;
procedure TForm1.Lier(iMot, iLien: integer);
var k : integer;
begin
k := chercheTab(iMot);
if k < 0 then
begin
SetLength(Liens, Length(Liens)+1); //extension du tableau principal
k := Length(Liens)-1;
SetLength(Liens[k], 1);
Liens[k][0] := iMot; //identifiant
k := PlaceTab(k); //nouvel emplacement
Inc(nLiens); //mise à jour du nombre de mots liés
end;
SetLength(Liens[k], Length(Liens[k])+1); //extension du tableau secondaire
Liens[k, Length(Liens[k])-1] := iLien; // lien
PlaceLien(k); //tri
end;
procedure TForm1.AffLiens;
var i, k : integer;
begin
if Length(Liens)>0 then
begin
ListBox1.Clear;
k := chercheTab(iMot);
if k>=0 then
for i :=1 to Length(Liens[k]) -1 do
ListBox1.Items.Add(AnsiToUTF8(listeMots[Liens[k, i]]));
ListBox2.Items := ListBox1.Items;
end;
end;
procedure TForm1.PlaceLien(k: integer);
var i, Tamp : integer;
begin
i := Length(Liens[k]) - 1;
while (Liens[k, i] < Liens[k, i-1]) and (i>1) do
begin
Tamp := Liens[k, i];
Liens[k, i] := Liens[k, i-1];
Liens[k, i-1] := Tamp;
Dec(i);
end;
end;
function TForm1.PlaceTab(k: integer): integer;
var i : integer;
Tamp : Array of integer;
begin
i := k;
while (Liens[i, 0] < Liens[i-1, 0]) and (i>1) do
begin
Tamp := Liens[i];
Liens[i] := Liens[i-1];
Liens[i-1] := Tamp;
dec(i);
end;
PlaceTab := i;
end;
function TForm1.SansAccent(rMot: string): string;
var i, j : integer;
//les variables sAcc et cAcc sont créées au démarrage
begin
SansAccent := '';
rMot := LowerCase(rMot);
for i:=1 to Length(rMot) do
begin
j := Pos(rMot[i], cAcc);
case j of
0 : SansAccent := SansAccent + rMot[i];
1..15 : SansAccent := SansAccent + sAcc[j];
end;
end;
end;
procedure TForm1.listeApprox(rechMot: string);
var i, k : integer;
referMot, testMot : string;
begin
SetLength(tabApprox, 0); //mise à zéro du tableau
referMot := SansAccent(rechMot);
if referMot>'' then
begin
//point de démarrage de la recherche : première lettre du mot
i := 0;
while SansAccent(listeMots[i][1]) < referMot[1] do inc(i);
//début de la recherche
repeat
if SansAccent(listeMots[i]) = referMot then
begin
listBox3.Items.Append(AnsiToUTF8(listeMots[i]));
//enregistrement des index pour réutilisation
SetLength(tabApprox, Length(tabApprox)+1);
tabApprox[Length(tabApprox)-1] := i;
end;
inc(i)
until (i>nMots-1) or (SansAccent(listeMots[i][1]) > referMot[1]);
//la première lettre a changé
end;
end;
procedure TForm1.MAJSupp;
begin
Label5.Caption := Label4.Caption;
ListBox4.Items := ListBox1.Items;
Label6.Caption:= '';
Button6.Enabled:= False;
end;
procedure TForm1.SuppLien(iMot, iLien: integer);
var i, k : integer;
begin
//dans le tableau secondaire de iMot, on supprime iLien
k := chercheTab(iMot);
i:=1;
while Liens[k, i]<> iLien do inc(i);
while (i<Length(Liens[k])-1) do
begin
Liens[k, i] := Liens[k, i+1];
inc(i);
end;
SetLength(Liens[k], Length(Liens[k]) - 1);
if (Length(Liens[k]) = 1) then //le mot n'a plus de lien
begin
for i := k to Length(Liens)-2 do
Liens[i] := Liens[i+1];
SetLength(Liens, Length(Liens)-1);
nLiens := Length(Liens);
end;
MAJBalayage;
end;
procedure TForm1.TrackBar1MouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
iMot := Round(TrackBar1.Position*nMots/1000);
iMot := (iMot + nMots) mod nMots;
MAJBalayage;
end;
procedure TForm1.Recherche(rechMot: string);
var irechMot : integer;
begin
listBox3.Clear;
irechMot := listeMots.IndexOf(rechMot);
if irechMot >= 0 then
begin
Label1.Caption:= AnsiToUTF8(listeMots[irechMot]);
iMot := irechMot;
MAJBalayage;
end
else
begin
Label1.Caption:= 'échec';
listeApprox(rechMot);
end;
end;
procedure TForm1.TabSheet1Show(Sender: TObject);
begin
MAJBalayage;
end;
procedure TForm1.TabSheet3Show(Sender: TObject);
begin
MAJBalayage;
end;
procedure TForm1.TabSheet4Show(Sender: TObject);
begin
MAJSupp;
end;
procedure TForm1.TabSheet5Show(Sender: TObject);
begin
Edit4.Caption := AnsiToUTF8(listeMots[iMot]);
end;
end.Et l'unité uDisque :
unit uDisque;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, Dialogs;
procedure LireFichier(listeMots : TStringList);
procedure regLiens;
procedure lireLiens;
procedure regFichier(listeMots : TStringList);
implementation
uses ulex8;
procedure LireFichier(listeMots: TStringList);
begin
//listeMots.LoadFromFile('liste.de.mots.francais.frgut.txt');
listeMots.LoadFromFile('liMots.txt');
end;
procedure regLiens;
var i, j, k : integer;
fLiens : file of integer;
begin
AssignFile(fLiens, 'fichLiens.bin');
{$I-}
Reset(fLiens);
{$I+}
ReWrite(fLiens, 1);
for i := 0 to Length(Liens)-1 do
begin
j := Length(Liens[i]);
Write(fLiens, j);
for k:=0 to j-1 do
Write(fLiens, Liens[i, k]);
end;
CloseFile(fLiens);
end;
procedure lireLiens;
var
j, m : integer;
fLiens : file of integer;
begin
nLiens := 0; //nombre de mots liés
AssignFile(fLiens, 'fichLiens.bin');
{$I-}
Reset(fLiens);
{$I+}
if IOResult<>0 then
ShowMessage('Fichier liens inexistant')
else
while not EOF(fLiens) do
begin
SetLength(Liens, nLiens+1); //taille du tableau principal
Read(fLiens, m);
SetLength(Liens[nLiens], m); //taille du tableau secondaire
for j := 0 to m-1 do
Read(fLiens, Liens[nLiens, j]); //remplissage du tableau secondaire
inc(nLiens); //nombre de mots liés
end;
CloseFile(fLiens);
end;
procedure regFichier(listeMots: TStringList);
begin
listeMots.SaveToFile('liMots.txt');
end;
end.








