tree.c
15.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
// --------------------------------------------------------
// Projet IMA3 2019 - Lecture d'une bibliothèque
// Décompte du nombre de fautes d'orthographe dans un texte
// Normand Quentin & Rouillé Guillaume
// --------------------------------------------------------
#include "tree.h"
// Fonction permettant de savoir si la structure est vide
bool is_empty_tree(Node Tree)
{
return(Tree==NULL);
}
// Fonction permettant de savoir si le tableau 'next' est un tableau de pointeurs NULL
bool is_leaf(Node Tree)
{
for(int i=0; i<NB_CARAC; i++)
if(Tree->next[i] != NULL)
return false;
return true;
}
// Initialisation de la structure accueillant le dictionnaire
void init_tree(Node* Tree)
{
if(is_empty_tree(*Tree))
{
*Tree = malloc(sizeof(node));
(*Tree)->letter = 7; // caractère sans utilité
(* Tree)->endWord = false;
for(int i=0; i<NB_CARAC; i++)
(*Tree)->next[i] = NULL; // initialisation du tableau 'next' à un tableau de pointeurs NULL
}
}
// Détermine l'indice de rangement dans le tableau 'next' du caractère 'letter'
int find_caract_indice(char letter)
{
if(letter>=97 && letter<=122) return letter-'a';
if(letter>=65 && letter<=90) return letter-'A';
if(letter == 39) return 26; // l'apostrophe est placée en 27ème position
else return -1;
}
// Fonction d'ajout d'un mot 'word' dans la structure 'tree' de type 'arbre indexé'
void add_in_tree(Node Tree, char word[])
{
int j=0; // indice du caractère dans le mot 'word'
int ind; // indice du caractère dans le tableau 'next'
Node Tree2 = Tree;
while(word[j] != '\0') // on parcourt tout le mot 'word'
{
char letter = word[j];
ind = find_caract_indice(letter); // on récupère l'indice de rangement de 'letter'
if(Tree2->next[ind]!=NULL) // si le pointeur du tableau 'next' correspondant au caractère lu n'est pas NULL, on s'insère dans cette 'branche' de l'arbre indexé et on continue avec le caractère suivant
{
Tree2 = Tree2->next[ind];
}
else // sinon, on ajoute une nouvelle cellule de type 'struct node' et on y insère les informations concernant le caractères
{
Node new = NULL;
new = malloc(sizeof(node));
new->letter = letter;
for(int i=0; i<NB_CARAC; i++)
{
new->next[i]=NULL;
}
new->endWord = false;
Tree2->next[ind] = new; // on fait pointé le tableau du caractère précédent vers cette cellule (vers ce caractère)
if(!(Tree2->endWord)) // si le caractère précédent n'est pas un caractère de fin, on lui donne la valeur 'false'
Tree2->endWord = false;
Tree2=Tree2->next[ind]; // on se place au niveau du caractère suivant dans l'arbre indexé
}
j++;
if(word[j]=='\0') // si le caractère suivant est la fin de la chaîne, on dit que le caractère précédent est un caractère de fin
Tree2->endWord = true;
}
}
// Fonction qui détermine si le caractère est un caractère de fin de mot (espace, ',', ';', '.', etc..)
bool is_end_caract(char letter)
{
if(letter==0) return true;
if((letter>=32 && letter<=38)||(letter>=40 && letter<=47)||(letter>=58 && letter<=64)||(letter>=123 && letter<=126)) return true;
return false;
}
// Renvoi l'indice maximum du mot 'word'
char max_index(char word[])
{
int index = 0;
while(!is_end_caract(word[index]))
index++;
return index;
}
// Fonction qui scanne les mots du fichier à vérifier et les analyse
void scan_word(Node Tree, char word[], int* error, FILE* fp_txt, int* correct)
{
bool endWord = false; // true : le caractère est un caractère de fin, false : le caractère n'est pas un caractère de fin
bool stop = false; // permet de ne pas compte 2 fois certaines erreurs
int ind = 0; // indice dans le mot 'word'
int indice; // indice de rangement de letter dans le tableau 'next'
char letter;
Node Tree2 = Tree;
while(!is_end_caract(word[ind])) // si 'word[ind]' n'est pas un caractère de fin
{
stop = false;
letter = word[ind];
indice = find_caract_indice(letter); // récupération de l'indice de rangement
if(Tree2 != NULL && Tree2->next[indice]!=NULL) // si l'arbre n'est pas vide et que la case correspondant à la lettre dans l'arbre n'est pas vide
{
ind++; // on passe à la lettre suivante
Tree2 = Tree2->next[indice]; // on se met dans la case de la lettre qui vient d'être vérifiée
endWord = Tree2->endWord;
}
else
{
add_error(error, word, ind, Tree2, fp_txt, correct); // si l'abre est vide ou que la case de l'arbre correspondant à la lettre est vide, on ajoute une erreur
ind = max_index(word); // on se place à la fin du mot pour ne pas continuer de vérifier ses lettres
stop = true;
endWord = false;
}
}
if(!endWord && !stop) // si un mot manque d'au moins une lettre (endWord == false alors qu'on a atteint la fin du mot à scanner)
{
add_error(error, word, ind, Tree2, fp_txt, correct); // on ajoute une erreur
}
}
// Ajoute une erreur, donne le type d'erreur et lance le processus de correction
void add_error(int* error, char word[], int index, Node Tree, FILE* fp_txt, int* correct)
{
(*error)++;
if(index==0) // si la première lettre n'est pas reconnu, on ne propose pas de correction
{
printf("Le mot '%s' ne correspond à aucun mot du dictionnaire.\n\n", word);
return;
}
else if(index<(int)strlen(word)) // si l'index d'erreur n'est pas le dernier indice du mot, on a une faute dans le mot
printf("Il y a une erreur dans le mot '%s', au caractère %c d'incide %d.\n", word, word[index], index);
else // sinon l'erreur a lieu à la fin du mot
printf("Il manque au moins une lettre au mot '%s'.\n", word);
make_correction(word, index, Tree, fp_txt, correct); // processus de correction
}
// Lance le processus de correction, avec les différentes initialisations
void make_correction(char word[], int index, Node Tree, FILE* fp_txt, int* correct)
{
correction liste = NULL; // crée une liste contigüe qui contiendra les propositions de correction
init_correction(&liste); // initalise cette liste
char end; // permet de stocker un éventuel caractère de fin (',', ';', '?', etc..)
bool testEnd; // true si le mot possede un caractère de fin, false sinon
testEnd = detect_end_caract(&end, word);
int longueur = strlen(word);
Node Tree2 = Tree;
word[index-1]='\0'; // on coupe le mot avant l'erreur
printf("\n");
char words[MAX] = ""; // chaine de caractères permettant le stockage des différentes lettres pouvant composer les mots de correction
make_tree_correct(Tree2, 0, words, word, liste); // rempli la liste chainée avec les propositions de correction
choice_word(liste, fp_txt, end, longueur, correct, testEnd); // permet le choix du mot de correction et lance le processus de remplacement
free(liste);
}
// Détermine si le mot word possède un caractère de fin et le renvoie si c'est le cas
bool detect_end_caract(char* end, char word[])
{
if(is_end_caract(word[strlen(word)-1]))
{
*end = word[strlen(word)-1];
return true;
}
return false;
}
// Permet le choix du mot de correction et lance le processus de remplacement
void choice_word(correction liste, FILE* fp_txt, char end, int longueur, int* correct, bool testEnd)
{
int choix;
printf("Voici les mots possibles pour corriger ce mot :\n");
for(int i=0; i<liste->dernier+1; i++) // on propose tous les mots de la liste contigüe
printf("%d. %s\n", i+1, liste->mots[i]);
printf("\n");
choix = -1;
printf("Quel mot voulez-vous sélectionner ? (Entrez %d pour ne pas corriger le mot)\n", (liste->dernier)+2); // exemple : 3 mots possibles. Si on entre 4, alors on ne veut pas corriger
while(choix<1 || choix>liste->dernier+2) // vérifie que la valeur entrée est correcte
{
scanf("%d", &choix);
}
if(choix==(liste->dernier)+2) // si on ne corrige pas
{
printf("\n");
return;
}
(*correct)++; // on incrémente un compteur de corrections
char word[MAX]; // contiendra le mot choisi
strcpy(word, liste->mots[choix-1]);
printf("Le mot %s a été sélectionné.\n\n", word);
if(testEnd) add_caract(word, end); // s'il y a un caractère de fin, on l'ajoute
correct_word(word, fp_txt, longueur); // processus de remplacement
}
// Remplace le mot comportant une erreur par le mot choisi dans choice_word
void correct_word(char word[], FILE* fp_txt, int longueur)
{
FILE* fp_tamp; // création d'un fichier tampon
fp_tamp = fopen("tampon.txt", "w+"); // ouverture en mode lecture/écriture avec suppression du contenu au préalable
if(fp_tamp==NULL)
{
printf("Erreur lors de l'ouverture du fichier tampon.\n");
return;
}
int position = ftell(fp_txt)-longueur; // on récupère la position dans le fichier à analyser du début du mot avec erreur
rewind(fp_txt); // on se place au début du fichier à scanner
char tempo;
while(ftell(fp_tamp)<position) // tant qu'on est pas arrivé au mot avec erreur
{
tempo = fgetc(fp_txt); // copie d'un caractère à la fois
if(tempo==EOF) break; // jusque la fin du fichier
fputc(tempo, fp_tamp); // on ajoute le caractère dans le fichier tampon
}
fprintf(fp_tamp, "%s", word); // on ajout le mot corrigé dans le fichier tampon
long newPosition = ftell(fp_tamp); // on récupère la position de la fin du mot corrigé
fseek(fp_txt, ftell(fp_txt)+longueur, SEEK_SET); // on se place après le mot avec erreur dans le fichier à analyser
while(1) // on copie la fin du fichier à analyser dans le fichier tampon
{
tempo = fgetc(fp_txt);
if(tempo==EOF) break;
fputc(tempo, fp_tamp);
}
rewind(fp_txt); // on se remet au debut des 2 fichiers pour renvoyer le contenu du fichier tampon dans le fichier à analyser
rewind(fp_tamp);
char temp[MAX_READ];
while(1) // on procède ligne par ligne car aucune modification sur un mot ne sera effectuée
{
if(fgets(temp, MAX_READ, fp_tamp)==NULL)
break;
fputs(temp, fp_txt);
}
fseek(fp_txt, newPosition, SEEK_SET); // on avait récupérer la position après le mot corrigé, on s'y place pour continuer l'analyse du fichier
fclose(fp_tamp); // fermeture du fichier tampon et suppression
remove("tampon.txt");
}
// Initialise la liste contigüe contenant les mots de correction
void init_correction(correction* liste)
{
if((*liste)!=NULL)
{
printf("Déjà initialisé.\n");
return;
}
*liste = malloc(sizeof(struct liste));
(*liste)->dernier = -1;
for(int i=0; i<NB_MOT_CORRECTION; i++)
{
strcpy((*liste)->mots[i], "");
}
}
// Affichage
void init_pgrm(void)
{
printf("\n------------------------------------------\n");
printf("Correcteur ortographique.\n");
printf("Normand Quentin & Rouillé Guillaume.\n");
printf("------------------------------------------\n\n");
}
// Renvoie true si 'word' ne comprend pas d'accent, false sinon
bool no_accent(char word[])
{
int index_max = max_index(word);
for(int i=0; i<index_max; i++)
if(word[i]<0) return false;
return true;
}
// Lecture du fichier à analyser
void read_txt(FILE* fp, Node* Tree, int* error, int* correct)
{
char word[MAX];
while(1)
{
if(fscanf(fp, "%s", word)!=1) // tant qu'on a pas fini la lecture du fichier
break;
if(no_accent(word)) // on ne prend pas en compte les mots accentués (voir compte-rendu pour explications)
scan_word(*Tree, word, error, fp, correct); // on scanne le mot
}
if(feof(fp)) printf("Fin de la lecture du fichier à scanner.\n\n");
if(ferror(fp)) printf("Erreur lors de la lecture du fichier à scanner.\n\n");
}
// Lecture du dictionnaire
void read_lib(FILE* fp, Node* Tree)
{
char word[MAX];
while(1)
{
if(fscanf(fp, "%s", word)!=1) // tant qu'on a pas fini la lecture du fichier
break;
if(no_accent(word)) // on ne prend pas en compte les mots accentués (voir compte-rendu pour explications)
add_in_tree(*Tree, word); // on ajoute le mot dans l'arbre indexé
}
if(feof(fp)) printf("Fin de la lecture de la bibliothèque.\n\n");
if(ferror(fp)) printf("Erreur lors de la lecture de la bibliothèque.\n\n");
}
// Affichage de l'arbre indexé (inutile pour le code, fonction d'aide)
void print_tree(Node Tree, int index, char word[])
{
if(is_empty_tree(Tree)) // si l'arbre est vide, rien a imprimer
return;
add_caract(word, Tree->letter); // sinon, on stocke le caractère dans une chaine
if(is_leaf(Tree)) // si on atteint une feuille, on imprime et on supprime le dernier caractère car il n'est plus utile
{
printf("%s\n", word);
supp_caract(word);
return;
}
if(Tree->endWord) // si on est à la fin d'un mot, on l'imprime mais on enlève pas de lettre car il peut y avoir des mots plus grands de même racine
printf("%s\n", word);
while(index < NB_CARAC) // on parcourt tout le tableau de pointeur de chaque cellule
{
if(!is_empty_tree(Tree->next[index])) // si la case du tableau n'est pas vide
{
print_tree(Tree->next[index], 0, word); // on relance la fonction en partant de la case
}
index++;
}
supp_caract(word); // on supprime le dernier caractère stocké car il n'est plus utile
return;
}
// Comme son nom l'indique, cette fonction sélectionne les mots possibles pour la correction du mot 'word' et les ajoute dans la liste contigüe
void make_tree_correct(Node Tree, int index, char word[], char start[], correction liste) // même principe que la fonction d'affichage de l'arbre indexé
{
if(is_empty_tree(Tree)) // si l'arbre est vide, il n'y a pas de correction possible
return;
add_caract(word, Tree->letter); // on ajoute le caractère à la chaine de stockage
if(is_leaf(Tree)) // si on arrive au bout d'une branche, on ajoute le mot dans la liste et on supprime le dernier caractère stocké
{
add_in_liste(liste, start, word);
supp_caract(word);
return;
}
if(Tree->endWord) // si on arrive à la fin d'un mot, on l'ajout dans la liste
{
add_in_liste(liste, start, word);
}
while(index < NB_CARAC) // on parcourt le tableau de chaque cellule
{
if(!is_empty_tree(Tree->next[index])) // si la case n'est pas vide
{
make_tree_correct(Tree->next[index], 0, word, start, liste); // on relance la fonction à partir de cette case
}
index++;
}
supp_caract(word); // on supprime le dernier caractère stocké, inutile maintenant
return;
}
// Ajoute les mots de correction dans la liste contigüe
void add_in_liste(correction liste, char start[], char word[])
{
char temp[MAX];
strcpy(temp, start); // on concatène le debut du mot (avant l'erreur) avec la suite envoyée dans 'word'
strcat(temp, word);
if(liste->dernier<NB_MOT_CORRECTION-1) // pour ne pas dépasser le nombre de mots proposés
{
(liste->dernier)++;
strcpy(liste->mots[liste->dernier], temp);
}
}
// Ajoute un caractère à une chaine de caractères
void add_caract(char word[], char caract)
{
char str[2];
str[0] = caract;
str[1] = '\0';
strcat(word, str);
}
// Supprime un caractère d'une chaine de caractères
void supp_caract(char word[])
{
word[strlen(word)-1]='\0';
}
// Désalloue la mémoire utilisée par l'arbre indexé
void free_tree(Node* Tree)
{
if(*Tree!=NULL)
{
for(int i=0; i<NB_CARAC; i++)
{
free_tree(&(*Tree)->next[i]);
}
free(*Tree);
}
}