diff --git a/solution_commentées_fin_exo_4/main.c b/solution_commentées_fin_exo_4/main.c new file mode 100644 index 0000000..388c138 --- /dev/null +++ b/solution_commentées_fin_exo_4/main.c @@ -0,0 +1,463 @@ +/** + * Carnet d'adresses en C + * + * Ce programme implémente un carnet d'adresses simple permettant de : + * - Ajouter des contacts avec leurs informations personnelles + * - Afficher la liste des contacts + * - Sauvegarder/charger les contacts depuis un fichier + * + * Concepts abordés : + * - Structures en C + * - Allocation dynamique de mémoire + * - Manipulation de fichiers + * - Saisie utilisateur + * - Structures imbriquées + */ + +#include // Entrées/sorties standard +#include // Pour malloc, realloc, etc. +#include // Pour strtok, etc. + +/* Définition des constantes */ +#define MAX_CONTACTS 100 // Nombre maximum de contacts initial +#define MAX_NOM 50 // Longueur maximale pour un nom +#define MAX_PRENOM 50 // Longueur maximale pour un prénom +#define MAX_TELEPHONE 15 // Longueur maximale pour un numéro de téléphone +#define MAX_RUE 100 // Longueur maximale pour le nom de rue +#define MAX_CODE_POSTAL 10 // Longueur maximale pour un code postal +#define MAX_VILLE 50 // Longueur maximale pour un nom de ville +#define MAX_PAYS 50 // Longueur maximale pour un nom de pays +#define MAX_TYPE_TEL 20 // Longueur maximale pour le type de téléphone +#define MAX_TELEPHONES 3 // Nombre maximum de téléphones par contact + +/* Définition des constantes pour le menu */ +#define CHOIX_AJOUTER 1 // Option pour ajouter un contact +#define CHOIX_AFFICHER 2 // Option pour afficher les contacts +#define CHOIX_RECHERHER 4 // Option pour rechercher (non implémentée) +#define CHOIX_QUITTER 27 // Option pour quitter le programme + +/** + * Structure pour stocker un numéro de téléphone + * Chaque téléphone contient un type (ex: "Mobile", "Travail") et un numéro + */ +typedef struct{ + char type[MAX_TYPE_TEL]; // Type de téléphone (Mobile, Domicile, etc.) + char numero[MAX_TELEPHONE]; // Numéro de téléphone +}Telephone; + +/** + * Structure pour stocker une adresse complète + * Contient les informations classiques d'une adresse postale + */ +typedef struct{ + char rue[MAX_RUE]; // Numéro et nom de rue + char codePostal[MAX_CODE_POSTAL]; // Code postal + char ville[MAX_VILLE]; // Ville + char pays[MAX_PAYS]; // Pays +}adresse; + +/** + * Structure principale d'un contact + * Contient les informations personnelles, téléphones et adresse + */ +typedef struct { + char nom[MAX_NOM]; // Nom de famille + char prenom[MAX_PRENOM]; // Prénom + int nbtel; // Nombre de téléphones pour ce contact + Telephone tel[MAX_TELEPHONES]; // Tableau de téléphones (max 3) + adresse lieu; // Adresse du contact +} Contact; + +/* Exemple de code commenté mais non exécuté (pour démonstration) +int a; +Contact truc; +truc.prenom = "valentin"; // Erreur: on ne peut pas affecter directement une chaîne à un tableau +truc.lieu.ville="limoges"; // Erreur: même problème +adresse mon_adresse; +truc.lieu=mon_adresse; // OK: affectation de structure à structure +*/ + +/* Prototypes des fonctions */ +/** + * Ajoute un nouveau contact dans le tableau de contacts + * @param contacts Tableau de contacts + * @param nbContacts Pointeur vers le nombre actuel de contacts + */ +void ajouterContact(Contact *contacts, int *nbContacts); + +/** + * Affiche tous les contacts existants + * @param contacts Tableau de contacts + * @param nbContacts Nombre actuel de contacts + */ +void afficherContacts(Contact *contacts, int nbContacts); + +/** + * Sauvegarde les contacts dans un fichier texte au format CSV + * @param contacts Tableau de contacts à sauvegarder + * @param nbContacts Nombre de contacts + * @param filename Nom du fichier de sauvegarde + */ +void sauvegarderContacts(Contact *contacts, int nbContacts, const char *filename); + +/** + * Charge les contacts depuis un fichier + * @param contacts Tableau de contacts à remplir + * @param nbContacts Pointeur vers le nombre de contacts (sera modifié) + * @param filename Nom du fichier à charger + * @param capacite Pointeur vers la capacité du tableau (pour agrandir si nécessaire) + */ +void chargerContacts(Contact *contacts, int *nbContacts, const char *filename, int *capacite); + +/** + * Agrandit le tableau de contacts quand il est plein + * @param contacts Pointeur vers le pointeur du tableau (double pointeur car on modifie le pointeur) + * @param capacite Pointeur vers la capacité actuelle (sera modifié) + */ +void agrandirTableauContacts(Contact **contacts, int *capacite); + +/** + * Fonction principale + */ +int main() { + Contact *contacts; // Déclaration du pointeur vers le tableau de contacts + + // Allocation dynamique initiale du tableau de contacts + contacts = malloc(MAX_CONTACTS * sizeof(Contact)); + + int capacite = MAX_CONTACTS; // Capacité initiale du tableau + + // Vérification de l'allocation mémoire + if (contacts == NULL) { + printf("L'allocation mémoire n'a pas marché \n"); + return 1; // Sortie avec code d'erreur + } + + int nbContacts = 0; // Initialisation du nombre de contacts + + // Chargement des contacts depuis le fichier + chargerContacts(contacts, &nbContacts, "contacts.txt", &capacite); + + int choix; + do { + // Affichage du menu + printf("\n%d. Ajouter un contact\n", CHOIX_AJOUTER); + printf("%d. Afficher les contacts\n", CHOIX_AFFICHER); + printf("%d. Quitter\n", CHOIX_QUITTER); + printf("Votre choix : "); + scanf("%d", &choix); + getchar(); // Pour éviter le problème de buffer avec scanf (consomme le '\n') + + // Traitement du choix de l'utilisateur + switch (choix) { + case CHOIX_AJOUTER: + ajouterContact(contacts, &nbContacts); + sauvegarderContacts(contacts, nbContacts, "contacts.txt"); + break; + case CHOIX_AFFICHER: + afficherContacts(contacts, nbContacts); + break; + case CHOIX_QUITTER: + printf("Au revoir !\n"); + break; + default: + printf("Choix invalide, veuillez réessayer.\n"); + } + } while (choix != 3); // ATTENTION: Incohérence avec CHOIX_QUITTER=27 + + // Libération de la mémoire (MANQUANTE dans le code original !) + // free(contacts); // Cette ligne devrait être ajoutée + + return 0; +} + +/** + * Fonction pour ajouter un contact au carnet d'adresses + * Demande à l'utilisateur de saisir toutes les informations nécessaires + */ +void ajouterContact(Contact *contacts, int *nbContacts) { + // Vérification si le tableau est plein + if (*nbContacts >= MAX_CONTACTS) { + printf("Le carnet d'adresses est plein !\n"); + return; + } + + // Saisie du nom + printf("Nom : "); + fgets(contacts[*nbContacts].nom, 50, stdin); + strtok(contacts[*nbContacts].nom, "\n"); // Supprime le caractère newline + + // Saisie du prénom + printf("Prénom : "); + fgets(contacts[*nbContacts].prenom, 50, stdin); + strtok(contacts[*nbContacts].prenom, "\n"); + + // Saisie du nombre de téléphones + printf("Combien de numéros de téléphone à ajouter ? (min 1 max 3)"); + scanf("%d", &contacts[*nbContacts].nbtel); + getchar(); // Consomme le caractère newline + + // Validation du nombre de téléphones + while (contacts[*nbContacts].nbtel < 1 || contacts[*nbContacts].nbtel > 3) { + printf("Combien de numéros de téléphone à ajouter ? (min 1 max 3)"); + scanf("%d", &contacts[*nbContacts].nbtel); + getchar(); + } + + // Saisie des informations pour chaque téléphone + for (int i = 0; i < contacts[*nbContacts].nbtel; i++) { + printf("Téléphone n° %d: ", i+1); + fgets(contacts[*nbContacts].tel[i].numero, 15, stdin); + strtok(contacts[*nbContacts].tel[i].numero, "\n"); + + printf("Type du téléphone n° %d: ", i+1); + fgets(contacts[*nbContacts].tel[i].type, 20, stdin); + strtok(contacts[*nbContacts].tel[i].type, "\n"); + } + + // Saisie des informations d'adresse + printf("Rue: "); + fgets(contacts[*nbContacts].lieu.rue, 100, stdin); + strtok(contacts[*nbContacts].lieu.rue, "\n"); + + printf("Code Postal: "); + fgets(contacts[*nbContacts].lieu.codePostal, 10, stdin); + strtok(contacts[*nbContacts].lieu.codePostal, "\n"); + + printf("ville: "); + fgets(contacts[*nbContacts].lieu.ville, 50, stdin); + strtok(contacts[*nbContacts].lieu.ville, "\n"); + + printf("Pays: "); + fgets(contacts[*nbContacts].lieu.pays, 50, stdin); + strtok(contacts[*nbContacts].lieu.pays, "\n"); + + // Incrémentation du nombre de contacts + (*nbContacts)++; +} + +/** + * Fonction pour afficher tous les contacts enregistrés + * Le format d'affichage dépend du nombre de téléphones + */ +void afficherContacts(Contact *contacts, int nbContacts) { + // Vérification s'il y a des contacts à afficher + if (nbContacts == 0) { + printf("Aucun contact enregistré.\n"); + return; + } + + // Parcours et affichage de tous les contacts + for (int i = 0; i < nbContacts; i++) { + // Format d'affichage différent selon le nombre de téléphones + switch (contacts[i].nbtel) { + case 1: + // Contact avec 1 téléphone + printf("%d. %s %s - %s:%s - %s %s %s %s\n", i + 1, + contacts[i].nom, + contacts[i].prenom, + contacts[i].tel[0].type, + contacts[i].tel[0].numero, + contacts[i].lieu.rue, + contacts[i].lieu.codePostal, + contacts[i].lieu.ville, + contacts[i].lieu.pays); + break; + case 2: + // Contact avec 2 téléphones + printf("%d. %s %s - %s:%s - %s:%s - %s %s %s %s\n", i + 1, + contacts[i].nom, + contacts[i].prenom, + contacts[i].tel[0].type, + contacts[i].tel[0].numero, + contacts[i].tel[1].type, + contacts[i].tel[1].numero, + contacts[i].lieu.rue, + contacts[i].lieu.codePostal, + contacts[i].lieu.ville, + contacts[i].lieu.pays); + break; + case 3: + // Contact avec 3 téléphones + printf("%d. %s %s - %s:%s - %s:%s- %s:%s - %s %s %s %s\n", i + 1, + contacts[i].nom, + contacts[i].prenom, + contacts[i].tel[0].type, + contacts[i].tel[0].numero, + contacts[i].tel[1].type, + contacts[i].tel[1].numero, + contacts[i].tel[2].type, + contacts[i].tel[2].numero, + contacts[i].lieu.rue, + contacts[i].lieu.codePostal, + contacts[i].lieu.ville, + contacts[i].lieu.pays); + break; + } + } +} + +/** + * Fonction pour sauvegarder les contacts dans un fichier CSV + * Le format de sauvegarde dépend du nombre de téléphones + */ +void sauvegarderContacts(Contact *contacts, int nbContacts, const char *filename) { + // Ouverture du fichier en mode écriture + FILE *file = fopen(filename, "w"); + if (!file) { + printf("Erreur d'ouverture du fichier.\n"); + return; + } + + // Écriture de chaque contact dans le fichier + for (int i = 0; i < nbContacts; i++) { + // Format d'écriture différent selon le nombre de téléphones + switch (contacts[i].nbtel) { + case 1: + // Contact avec 1 téléphone (les champs vides sont représentés par des ';' consécutifs) + fprintf(file, "%s;%s;%s;%s;;;;;%s;%s;%s;%s\n", + contacts[i].nom, + contacts[i].prenom, + contacts[i].tel[0].type, + contacts[i].tel[0].numero, + contacts[i].lieu.rue, + contacts[i].lieu.codePostal, + contacts[i].lieu.ville, + contacts[i].lieu.pays); + break; + case 2: + // Contact avec 2 téléphones + fprintf(file, "%s;%s;%s;%s;%s;%s;;;%s;%s;%s;%s\n", + contacts[i].nom, + contacts[i].prenom, + contacts[i].tel[0].type, + contacts[i].tel[0].numero, + contacts[i].tel[1].type, + contacts[i].tel[1].numero, + contacts[i].lieu.rue, + contacts[i].lieu.codePostal, + contacts[i].lieu.ville, + contacts[i].lieu.pays); + break; + case 3: + // Contact avec 3 téléphones + fprintf(file, "%s;%s;%s;%s;%s;%s;%s;%s;%s;%s;%s;%s\n", + contacts[i].nom, + contacts[i].prenom, + contacts[i].tel[0].type, + contacts[i].tel[0].numero, + contacts[i].tel[1].type, + contacts[i].tel[1].numero, + contacts[i].tel[2].type, + contacts[i].tel[2].numero, + contacts[i].lieu.rue, + contacts[i].lieu.codePostal, + contacts[i].lieu.ville, + contacts[i].lieu.pays); + break; + } + } + + // Fermeture du fichier + fclose(file); +} + +/** + * Fonction pour charger les contacts depuis un fichier CSV + * Gère les différents formats selon le nombre de téléphones + */ +void chargerContacts(Contact *contacts, int *nbContacts, const char *filename, int *capacite) { + // Ouverture du fichier en mode lecture + FILE *file = fopen(filename, "r"); + if (!file) { + printf("Fichier introuvable\n"); + return; + } + + // Initialisation du compteur de contacts + *nbContacts = 0; + char ligne[500]; // Buffer pour lire une ligne du fichier + + // Lecture du fichier ligne par ligne + while (*nbContacts < *capacite && fgets(ligne, sizeof(ligne), file)) { + // Tentative de lecture d'un contact avec 1 téléphone + if (sscanf(ligne, "%49[^;];%49[^;];%19[^;];%14[^;];;;;;%99[^;];%9[^;];%49[^;];%49[^\n]", + contacts[*nbContacts].nom, + contacts[*nbContacts].prenom, + contacts[*nbContacts].tel[0].type, + contacts[*nbContacts].tel[0].numero, + contacts[*nbContacts].lieu.rue, + contacts[*nbContacts].lieu.codePostal, + contacts[*nbContacts].lieu.ville, + contacts[*nbContacts].lieu.pays) == 8) + { + contacts[*nbContacts].nbtel = 1; + (*nbContacts)++; + } + // Tentative de lecture d'un contact avec 2 téléphones + else if (sscanf(ligne, "%49[^;];%49[^;];%19[^;];%14[^;];%19[^;];%14[^;];;;%99[^;];%9[^;];%49[^;];%49[^\n]", + contacts[*nbContacts].nom, + contacts[*nbContacts].prenom, + contacts[*nbContacts].tel[0].type, + contacts[*nbContacts].tel[0].numero, + contacts[*nbContacts].tel[1].type, + contacts[*nbContacts].tel[1].numero, + contacts[*nbContacts].lieu.rue, + contacts[*nbContacts].lieu.codePostal, + contacts[*nbContacts].lieu.ville, + contacts[*nbContacts].lieu.pays) == 10) + { + contacts[*nbContacts].nbtel = 2; + (*nbContacts)++; + } + // Tentative de lecture d'un contact avec 3 téléphones + else if (sscanf(ligne, "%49[^;];%49[^;];%19[^;];%14[^;];%19[^;];%14[^;];%19[^;];%14[^;];%99[^;];%9[^;];%49[^;];%49[^\n]", + contacts[*nbContacts].nom, + contacts[*nbContacts].prenom, + contacts[*nbContacts].tel[0].type, + contacts[*nbContacts].tel[0].numero, + contacts[*nbContacts].tel[1].type, + contacts[*nbContacts].tel[1].numero, + contacts[*nbContacts].tel[2].type, + contacts[*nbContacts].tel[2].numero, + contacts[*nbContacts].lieu.rue, + contacts[*nbContacts].lieu.codePostal, + contacts[*nbContacts].lieu.ville, + contacts[*nbContacts].lieu.pays) == 12) + { + contacts[*nbContacts].nbtel = 3; + (*nbContacts)++; + } + + // Vérification si le tableau est plein et agrandissement si nécessaire + if (*nbContacts >= *capacite) { + agrandirTableauContacts(&contacts, capacite); + } + } + + // Fermeture du fichier + fclose(file); + printf("%d contacts chargés avec succès.\n", *nbContacts); +} + +/** + * Fonction pour agrandir le tableau de contacts + * Double la capacité du tableau quand il est plein + */ +void agrandirTableauContacts(Contact **contacts, int *capacite) { + // Calcul de la nouvelle capacité (double de l'ancienne) + int nouvelleCapacite = (*capacite) * 2; + + // Réallocation du tableau avec la nouvelle taille + Contact *pointeurContactTemporaire = realloc(*contacts, nouvelleCapacite * sizeof(Contact)); + + // Vérification si la réallocation a réussi + if (pointeurContactTemporaire == NULL) { + printf("La réallocation mémoire n'a pas fonctionné\n"); + } else { + // Mise à jour de la capacité et du pointeur + *capacite = nouvelleCapacite; + *contacts = pointeurContactTemporaire; + printf("Réallocation mémoire réussie. Nouvelle capacité = %d\n", *capacite); + } +} \ No newline at end of file