Skip to content

EPST 2e année – TP4 Exercice 1

26 novembre 2012

Le TP N°4 propose deux exercices pour vous entraîner avec les tableaux et matrices.

Énoncé

Solution du 1er exercice

Voici ma solution du 1er exercice :

/*
 *  EPST 2e année - 2012-2013
 *  TP4 - Tableaux et matrices
 *  Exercice 1 : suppression de caractères dans une chaîne
 *  Fichier source : supprimecar.c
 *  Compiler avec :
 *  gcc -Wall -o supprimecar supprimecar.c -std=c99
 *  Auteur : Amine "nh2" Brikci-Nigassa
 */
 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libgen.h>

#define LIGNE 80

/* utilisation : affiche l'aide sur l'utilisation du programme
 * Paramètre :
 *  - nom_prog : nom du programme
 */
void utilisation(char *nom_prog){
    printf("Utilisation : %s [TEXTE  CHAINE]\n",nom_prog);
    printf("Supprime d'un TEXTE la 1re occurrence des caractères se ");
    puts("trouvant dans une CHAINE\net affiche le résultat.");
    puts("Mettre TEXTE entre guillemets s'il contient des espaces.");
    printf("Utilisé sans paramètres, le programme demande les ");
    puts("chaînes de caractères de façon interactive.");
}

/* suppr_caract : supprime d'un texte la 1re occurrence des caractères
 *                se trouvant dans une chaîne
 * Paramètres :
 *  - texte : chaîne de caractères à modifier
 *  - asupprimer : contient les caractères à supprimer de texte
 */ 
void suppr_caract(char *texte, char *asupprimer){
    for (char *p = asupprimer ; *p != 0; ++p){
        char *q;
        for (q = texte ; (*q != 0) && (*q != *p) ; ++q)
            ;
        while (*q != 0){
            ++q;
            *(q-1) = *q;
        }
    }
}

int main(int argc, char **argv){
    if ((argc == 2) || (argc > 3))
        utilisation(basename(argv[0]));
    else if (argc == 3){
        suppr_caract(argv[1], argv[2]);
        puts(argv[1]);
    } else { // (argc == 1) 
        char texte[LIGNE], asupprimer[LIGNE];
        printf("Entrez la 1re chaîne de caractères (texte): ");
        fgets(texte, LIGNE, stdin);
        texte[strlen(texte)-1] = 0;  // élimine le '\n' qu'a pris fgets
        printf("Entrez la 2e chaîne de caractères (à supprimer): ");
        fgets(asupprimer, LIGNE, stdin);
        asupprimer[strlen(asupprimer)-1] = 0;
        suppr_caract(texte, asupprimer);
        printf("Chaîne résultat : %s\n", texte);
    }
    return EXIT_SUCCESS;
}

Explications

Dans la fonction suppr_caract :

  • la première boucle for parcourt la 2e chaîne : elle initialise un pointeur p avec l’adresse du début de la 2e chaîne et l’incrémente à chaque itération pour qu’il parcoure chacun de ses caractères jusqu’à trouver le caractère nul de fin.
  • la deuxième boucle for recherche le caractère à supprimer : elle est imbriquée dans la 1re for et fait parcourir au pointeur q la 1re chaîne en commençant par son début et en s’arrêtant quand il trouve le caractère à supprimer (quand le caractère pointé par q est le même que celui pointé par p) ou bien quand il arrive à la fin de la chaîne (quand q pointe vers un caractère nul)
  • la boucle while supprime le caractère trouvé : elle est toujours imbriquée dans la 1re for (mais pas dans la 2e). Quand elle démarre, si le caractère cherché a été trouvé, q pointe toujours dessus ;  il est alors incrémenté pour pointer sur le caractère suivant qui sera alors affecté au caractère qui le précède. L’opération est répétée jusqu’à la fin de la chaîne (caractère nul), ce qui donnera un décalage à gauche de tous les caractères qui suivent celui qui a été trouvé. Si le caractère n’avait pas été trouvé, q pointe sur le caractère nul final dès le début, et donc la boucle while ne fait rien.

Remarques

strcpy

Pour supprimer le caractère trouvé, j’ai utilisé une boucle while « faite maison », pourquoi n’ai-je pas utilisé strcpy ?

Il y a deux raisons. D’abord, c’est plus amusant de le faire soi-même, et ça ne prend que trois petites lignes (j’aurais pu le faire en une seule, savez-vous comment ?) .

La deuxième raison, la plus importante, c’est que je suis obéissant… Avant de penser à utiliser strcpy, j’ai tapé la commande man strcpy et j’ai trouvé « Les deux chaînes ne doivent pas se chevaucher. » Mince alors ! Je voulais faire strcpy(q, q+1) pour décaler les caractères qui suivent q vers la gauche mais les chaînes qui commencent par q et q+1 se chevauchent, elles ont tous leurs caractères en commun sauf un…

Je ne suis pas si obéissant que ça alors j’ai voulu savoir pourquoi c’était interdit. Après une petite recherche sur internet, j’ai compris que ça pouvait marcher comme ça pouvait donner des résultats anormaux. Ça dépend de la version du compilateur, de la taille de la chaîne, et de la chance que vous avez…

J’ai mis strcpy(q, q+1) dans mon programme et je l’ai essayé sur mon ordi avec GCC  version 4.7.2 sous GNU/Linux Ubuntu 12.10. J’ai d’abord pris l’exemple donné dans l’enoncé, ça a très bien marché. Ensuite j’ai essayé avec un texte plus grand et voici ce que ça a donné :

Entrez la première chaîne de caractères : abcdefghijklmnopqrstuvwxyz
Entrez la deuxième chaîne de caractères : fouad
Chaîne résultat : bceghijklmnprstvwwxyz
Le « q » a disparu et le « w » a été doublé ! (Ça ressemble à ce que j’avais trouvé sur internet). L’explication, c’est que dans les versions récentes, strcpy ne fait pas une copie simple comme avec la boucle while. Elle fonctionne de façon plus optimisée, peut-être en divisant la chaîne à copier en plusieurs morceaux qu’elle traite en parallèle pour aller plus vite (les processeurs actuels peuvent faire plusieurs tâches à la fois), mais si les chaînes se chevauchent, un caractère d’un morceau pourra être copié dans une case d’un autre morceau qui contient un caractère qui n’a pas encore été copié… enfin, c’est comme ça que je l’ai compris.
Conclusion : si le manuel d’une fonction dit de ne pas faire une chose, il ne faut pas la faire !

Paramètres de main()

Dernière chose, vous remarquez que dans ce programme la fonction main() a deux paramètres alors que d’habitude je n’en mettais pas. Dans la norme actuelle du C la déclaration de main() doit être :

  • soit int main(void), si le programme ne s’attend pas à recevoir des paramètres à l’exécution : c’est le cas des programmes des TP précédents, pour lesquels l’utilisateur entre les données à traiter de façon interactive, quand le programme les lui demande (avec scanf())
  • soit int main(int argc, char **argv), quand le programme récupère les données à traiter à partir des paramètres qui lui sont passés. C’est le cas de beaucoup de programmes fournis avec le système. Exemple : pour utiliser le programme mkdir, on doit lui donner en paramètre le nom du ou des répertoires que l’on veut qu’il crée. Si la fonction main() de mkdir avait été déclarée avec void en paramètre, il faudrait taper d’abord mkdir puis Entrée, puis attendre que le programme nous demande le nom du répertoire à créer, puis le taper et taper Entrée. C’est quand même mieux comme c’est.

Mais dans beaucoup de cas, le programme peut être utilisé des deux façons. C’est-à-dire qu’on peut l’appeler sans paramètres (et lui donner des informations à l’exécution) ou bien avec certains paramètres.

Par exemple, les programmes kwrite et gedit peuvent être appelés en leur donnant le nom du fichier à créer en paramètre, et ainsi on n’aura pas besoin de le faire pour enregistrer ; mais si on appelle ces programmes sans paramètre, il est possible de donner le nom au moment d’enregistrer le fichier.

C’est ce que j’ai choisi de faire dans ce programme:

  • S’il est exécuté comme d’habitude, en donnant juste le nom du fichier exécutable comme commande, il demandera à l’utilisateur les deux chaînes de caractères avec des scanf().
  • Si l’utilisateur préfère donner les chaînes de caractère avec la ligne de commande, il tapera : $ ./supprimecar chaine1 chaine2 alors le programme ne lui demandera rien d’autre et donnera directement le résultat.

Les paramètres argc et argv permettent de connaître les paramètres qui ont été passés.

  • argc est un entier qui renseigne sur le nombre de paramètres + 1 : si le programme a été lancé sans paramètres, argc vaudra 1, avec un paramètre, il vaut 2, etc.
  • argv est de type char ** et il s’utilise en fait comme un tableau de chaînes de caractères : la première chaîne argv[0] est le nom du programme tel qu’il a été donné dans la commande (ça correspond en général à ./supprimecar) et si argc>1 les chaînes suivantes sont les paramètres qui ont été passés (argv[1] = chaine1, argv[2] = chaine2)

Dans le cas où l’utilisateur a donné un seul paramètre ou plus de deux, le programme ne fera rien d’autre qu’afficher son « mode d’emploi », pour expliquer à l’utilisateur à quoi il sert et comment taper la commande pour l’exécuter.

Solution du 2e exercice

C’est par là.

Publicités
Laisser un commentaire

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s

%d blogueurs aiment cette page :