// -------------------------------------------------------- // 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; inext[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; inext[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; inext[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; idernier+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)dernier = -1; for(int i=0; imots[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; iletter); // 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->dernierdernier)++; 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; inext[i]); } free(*Tree); } }