IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

 C Discussion :

Permettre à l’utilisateur du programme de modifier une chaîne de caractères originale (ch1)


Sujet :

C

  1. #1
    Invité
    Invité(e)
    Par défaut Permettre à l’utilisateur du programme de modifier une chaîne de caractères originale (ch1)
    Bonjour, voici un exercice donné par mon formateur et que j'essaie de faire avec mes camarades à l'université. On fait de notre mieux pour apprendre tous les jours à correctement commenter notre code. Merci pour vos retours car on ne sait pas si le programme écrit en C va tourner...On travaille en 3 parties :lire les données en entrée, calculer les données, afficher les données (le résultat)

    Voici la consigne :
    Permettre à l’utilisateur du programme de modifier une chaîne de caractères originale (ch1) avec les fonctionnalités suivantes :
    -suppression de toutes les apparitions au sein de la chaîne originale, de la sous-chaîne constituée d’une
    suite de 4 caractères entrés au clavier par l’utilisateur.
    La suppression ne sera mise en œuvre que si la sous-chaîne est constituée des 4 caractères entrés au
    clavier avec le même ordre que celui observé lors de leur saisie;
    - le nombre de suppressions effectuées au sein de la chaîne initiale doit être affiché ou, le cas échéant,
    un message indiquant qu’il n’y a eu aucune suppression.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
     
    //Partie pour lire les données
     
    #include <stdio.h> //préprocesseur qui inclue les fichiers h dans c
    #include <stdlib.h> //lit le contenu de <stdlib.h>
    #include <string.h> //inclue la bibliothèque contenant strlen()
    #define N 128
     
    //déclaration de la variable supprime avec son paramètre char qui contient chaque argument de supprime
    int supprime (char * ); // on n'est pas sûr s'il faudrait écrire le *
     
    //déclaration de la fonction principale du programme avec ses parametres : 
    /* Le nombre d'arguments ENTIER sur la ligne de commande, et le tableau de chaînes de caractères (***chacune contenant un des arguments***).*/
    int main(int argc, char *argv[]) {
     char ch1 [N]; //déclaration de ch1 le tableau de chaines decaractères dont la composante vaut N
     int nbsuppression = 0; //initialisation de nbsuppression
     
     printf(" Entrez la chaine originale contenant maximum %d caracteres :\n\n ", N - 1);
     gets(ch1); // lire la chaine de caractères dans l'ordre
     nbsuppression = supprime (ch1); //affectation
     if (nbsuppression) //si nbsuppression est vraie alors...
     {
     printf("Voici le nombre de suppression(s) effectuee(s) : %d\n", nbsuppression);
     printf("Chaine modifiee :");
     puts(ch1);  //la fonction puts definie dans <stdio.h> va permettre d'écrire le string pour l'output
     }
     else
     printf("Aucune suppression effectuee dans la chaine originale.\n"); 
     
     system("PAUSE");
     return 0;
    }
     
     
     
     
    //Partie pour calculer les données
    //déclaration de la fonction supprime avec son paramètre s qui est la chaine de caractère contenant chaque argument de supprime
     
    int supprime (char * s) { // on ne comprend pas pourquoi il faudrait écrire * avant le s
     
    //initialisation des variables
     int cpt = 0, indlect = 0, indecrit = 0, lgchaine = strlen(s); //lgchaine pour la longueur effective
     char srecherchee[5];
     
     printf("Entrez une sous chaine de 4 caracteres a supprimer : \n");
     gets(srecherchee);
     
     
    //boucle while
    //utilisation d'une indice lecture et d'uneindiced'écriture
     while (indlect <= (lgchaine - 4)) // aussi longtemps qu'il restera au moins 4 caractères avant la fin de chaîne
     {
     if ( (*(s + indlect) == *srecherchee) 
     && (*(s + indlect + 1) == *(srecherchee + 1))
     && (*(s + indlect + 2) == *(srecherchee + 2))
     && (*(s + indlect + 3) == *(srecherchee + 3)) )
     {
     cpt++; 
     indlect = indlect + 4;
     }
     else
     {
     *(s + indecrit) = *(s + indlect);
     indlect++;
     indecrit++;
     }
    }
     
     
     
     
    //Partie pour afficher le résultat
     while (*(s + indlect))   // on n'arrive pas à se décider où mettre le * ni à quoi ça pourrait servir
     {
     *(s + indecrit) = *(s + indlect); //indice d'écriture reçoit la valeur de l'indice de lecture
     indlect++;
     indecrit++;
     
     } 
     *(s + indecrit) = '\0'; 
     
     return cpt;
    Merci de nous avoir lu.

  2. #2
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 690
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 690
    Points : 30 984
    Points
    30 984
    Billets dans le blog
    1
    Par défaut
    Bonjour

    Alors pour commencer, parlons des commentaires. Commenter c'est bien. C'est même super bien. Mais commenter bêtement c'est idiot, fatiguant, et aussi bien inutile pour celui qui code que pour celui qui lit. Exemple if (nbsuppression) //si nbsuppression est vraie alors...
    Commenter ce n'est pas réécrire l'instruction en français, c'est expliquer ce que va faire une fonction, puis dans la fonction comment on fait telle étape et telle étape.

    Un exemple
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    // Fonction qui compte les majuscules d'une chaine
    unsigned int nbMaj(char* s) {
    	unsigned int nb;
     
    	// On va compter les caractères qui sont avant le '\0' terminant la chaine
    	nb=0;
    	for (char *pt=s; *pt != '\0'; pt++) {
    		// On regarde si la lettre en cours est une majuscule
    		if (isupper(*pt) nb++;
    	}
     
    	// Travail terminé
    	return nb;
    }
     
    // Fonction qui indique si une année est ou n'est pas bissextile
    int isBissext(unsigned short year) {
    	// Une année est bissextile si elle est divisible par 4 mais pas par 100 ; ou alors par 400
    	return ((year%4) == 0 && (year%4) != 0) || (year%100) == 0;
    }

    Maintenant le code
    Tout d'abord on n'utilise plus gets() pour faire saisir une chaine. Parce que la fonction n'est pas limitée et ne bloque rien. Si ta chaine fait 10 caractères, rien n'interdit d'en taper 500. Les 490 iront corrompre la mémoire de ton code et c'est ce qu'on nomme un comportement indéterminé. Pous faire simple: pour aller le plus vite possible, le C ne vérifie absolument pas la cohérence de tes instructions, il les exécute telles que demandées. Mais si le résultat est logiquement incohérent, alors la norme ne garantit absolument rien quant au résultat. Ca peut marcher, ça peut planter, ça peut marcher les jours pairs et planter les jours impairs, ça peut éteindre ton ordi ou reformater ton dd (ok j'exagère car ton OS a des sécurités mais c'est pour l'exemple).
    La philosophie du programmeur C c'est "le programmeur sait ce qu'il fait sinon tant pis".

    Et donc depuis plusieurs années les fonctions non sécurisées et qui sont en contact avec l'extérieur sont black listées, surtout si un équivalent sécurisé existe. Et ici c'est fgets() à laquelle on donne une taille et qui s'arrête dès que la taille est atteinte (qui s'arrête en réalité 1 caractère avant car elle réserve une place pour le '\0').
    Donc remplacer gets(ch1) par fgets(ch1, N, stdin).

    Ensuite, à propos du '\0', une habitude des programmeurs est d'indiquer aux autres qu'ils y ont pensé. Et ils font ça de cette façon: char ch1[N+1]. Ainsi on voit de suite que ch1 aura N caractères utiles et un caractère pour le '\0'. Et la saisie devient alors fgets(ch1, N+1, stdin).

    Et enfin pour l'étoile ben oui, il faut la mettre. Parce que la fonction reçoit en paramètre une string, et qu'en C une string c'est un tableau de caractère mais qu'un tableau se manipule juste en passant l'adresse de son premier élément. Comme il est assuré que les éléments se suivent, avec l'adresse du premier, on a accès à tous les autres.
    Donc si ch1[] est un tableau de char, alors ch1 (le nom sans les crochets) référence l'adresse du premier élement (donc ch1 <=> &ch1[0]) et l'adresse d'un char se stocke dans un pointeur de char donc (en langage C) un "char étoile" => int supprime (char * s) signifiant "s est un char étoile" (selon la règle de base "le type d'une variable se lit avant le nom de la variable") et que personnellement je préfère écrire int supprime (char* s).
    Et si le retour est assurément positif, alors penser que unsigned n'a pas été fait pour être ignoré et qu'avec un "unsigned" les calculs sont plus rapides (à cause du bit de signe qu'on n'a pas besoin de prendre en compte) => unsigned int supprime (char* s).
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  3. #3
    Invité
    Invité(e)
    Par défaut
    Merci pour toutes ces précieuses informations. Merci également pour les 2 fonctions montrées en exemples c'est très aimable. Vos remarques font réfléchir.

    Puis-je par ailleurs vous demander une dernière chose svp ? Comment vous liriez en français la première boucle while (ligne 52 à 68) svp car je me sens un peu perdu pour ma part en ne visualisant pas trop ce qui s'y passe (en d'autres termes, je ne comprends pas comment me représenter mentalement ce qui se passe au sein de cette boucle while par exemple à l'aide d'un croquis...).
    Voici le code :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
     
    while (indlect <= (lgchaine - 4))
     {
     if ( (*(s + indlect) == *srecherchee)
     && (*(s + indlect + 1) == *(srecherchee + 1))
     && (*(s + indlect + 2) == *(srecherchee + 2))
     && (*(s + indlect + 3) == *(srecherchee + 3)) )
     {
     cpt++; 
     indlect = indlect + 4;
     }
     else
     {
     *(s + indecrit) = *(s + indlect);
     indlect++;
     indecrit++;
     }
    }
    Voici ce que je comprends pour ma part (comment j'interprète cette boucle) :
    "Aussi longtemps qu'il restera au moins 4 caractères avant la fin de chaîne (du string), si en cas de test le contenu (l'élément) de chacune des 4 indices correspondent au s recherchée par l'utilisateur, alors l'indice de lecture s'incrémente de 4 position vers la droite. Sinon, l'indice d'écriture pointera au même endroit que l'indice d'écriture puisqu'il ne sera pas question du caractère s recherchée par l'utilisateur. "
    Merci de m'avoir suivi sur ce dernier point où je bloque un peu.


    Mise à jour car j'ai oublié de mentionner un point à la ligne 47 :
    Du coup, serait-il plus pertinent de remplacer gets (srecherchee); par fgets(srecherchee); s'il vous plait ?
    Dernière modification par Invité ; 19/11/2022 à 22h35.

  4. #4
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 690
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 690
    Points : 30 984
    Points
    30 984
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par environnementBash Voir le message
    Comment vous liriez en français la première boucle while (ligne 52 à 68) svp car je me sens un peu perdu pour ma part en ne visualisant pas trop ce qui s'y passe (en d'autres termes, je ne comprends pas comment me représenter mentalement ce qui se passe au sein de cette boucle while par exemple à l'aide d'un croquis...).

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    while (indlect <= (lgchaine - 4)) {
    	if (
    		(*(s + indlect) == *srecherchee)
    		&& (*(s + indlect + 1) == *(srecherchee + 1))
    		&& (*(s + indlect + 2) == *(srecherchee + 2))
    		&& (*(s + indlect + 3) == *(srecherchee + 3))
    	) {
    		cpt++; 
    		indlect = indlect + 4;
    	}
    	else {
    		*(s + indecrit) = *(s + indlect);
    		indlect++;
    		indecrit++;
    	}
    }
    Un indice de lecture, un autre d'écriture ; et tous deux montent en parallèle. Sauf que si on trouve 4 caractères de "srecherche" à une certaine position de "s" (exemple "efgh" dans "abcdefghij") ça décale l'indice de lecture d'autant (donc ça les saute) sinon ça recopie les caractères de "s" dans "s" mais soit à la même position, soit à une position placée avant (donc ça rapproche le reste). Ici au final "s" contient "abcdij".
    Mais celui qui a écrit ce code s'imagine que ça va plus vite de faire ainsi parce qu'il a lu quelque part que les pointeurs vont plus vite que les accès par crochet.
    Sauf qu'il n'a pas pigé qu'un pointeur va plus vite à condition qu'il pointe vers le bon emplacement (comme dans mon exemple où je compte les majuscules, j'ai "pt" qui est directement positionné au bon endroit). Si on doit lui faire faire un calcul (comme ici), alors ça ne sert plus à rien et ça obscurcit inutilement le code.

    Bref, ce code peut s'écrire tout aussi bien ainsi...
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    while (indlect <= (lgchaine - 4)) {
    	if (
    		s[indlect] == srecherchee[0]
    		&& s[indlect+1] == srecherchee[1]
    		&& s[indlect+2] == srecherchee[2]
    		&& s[indlect+3] == srecherchee[3]
    	) {
    		cpt++; 
    		indlect+=4;
    	}
    	else {
    		s[indecrit]=s[indlect];
    		indlect++;
    		indecrit++;
    	}
    }
    ... il sera bien plus lisible et ira tout aussi vite. Et si strncmp() est autorisé alors c'est encore plus simple
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    while (indlect <= (lgchaine - 4)) {
    	if (strncmp(s + indlect, srecherche, 4) == 0) {
    		cpt++; 
    		indlect+=4;
    	}
    	else {
    		s[indecrit]=s[indlect];
    		indlect++;
    		indecrit++;
    	}
    }
    Et pour la lisibilité, les tabulations sont tout aussi importantes que les commentaires.

    Citation Envoyé par environnementBash Voir le message
    Du coup, serait-il plus pertinent de remplacer gets (srecherchee); par fgets(srecherchee);
    Ben oui !!! fgets(srecherche, 4+1, stdin) comme pour l'autre. Aucun gets() nulle part.
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  5. #5
    Invité
    Invité(e)
    Par défaut
    Merci pour tout ça apporte de la clarté.

    Du coup je me demande si il ne faudrait pas logiquement #include <stdin.h> tout en haut du code si je veux me permettre de remplacer tous les gets par fgets(...) ? Ce n'est pas l'énoncé mais ça me pose question...

  6. #6
    Membre expérimenté Avatar de edgarjacobs
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Mai 2011
    Messages
    625
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 63
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Mai 2011
    Messages : 625
    Points : 1 559
    Points
    1 559
    Par défaut
    Il n'y a pas de stdin.h . Par contre, il y a stdio.h dans lequel stdin est déclaré.
    On écrit "J'ai tort" ; "tord" est la conjugaison du verbre "tordre" à la 3ème personne de l'indicatif présent

  7. #7
    Invité
    Invité(e)
    Par défaut
    Citation Envoyé par edgarjacobs Voir le message
    Il n'y a pas de stdin.h . Par contre, il y a stdio.h dans lequel stdin est déclaré.
    Merci !

  8. #8
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 690
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 690
    Points : 30 984
    Points
    30 984
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par environnementBash Voir le message
    Ce n'est pas l'énoncé
    Hé non, un énoncé ne te dira pas comment faire les choses mais les choses à faire. A toi de trouver et mettre ce qu'il faut.
    Mais toutes les fonctions standard possèdent un manuel qui 1) explique la fonction et 2) donnent les includes à mettre. Si t'es sous Linux tu tapes man fgets et si t'es pas sous Linux tu dois avoir son équivalent dans l'éditeur/compilateur C que tu utilises. Et quoi qu'il en soit le "man" est aussi sur le net.
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  9. #9
    Invité
    Invité(e)
    Par défaut
    Je vous remercie Sve@r pour toutes ces informations.

    Bon dimanche !

  10. #10
    Invité
    Invité(e)
    Par défaut
    quelque chose m'est revenue entre temps: est-ce que écrire indlect = indlect + 4; à la ligne 60 peut très bien s'écrire ici comme indlect += 4; svp ? j'ai vu ça dans les mode d'emploi et des tutos en C.

    si je comprends bien la ligne que j'ai écrite, l'indice de lecture saute de 4 cases (est incrémenté ou décalé de 4 cases). Merci

  11. #11
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 690
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 690
    Points : 30 984
    Points
    30 984
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par environnementBash Voir le message
    j'ai vu ça dans les mode d'emploi et des tutos en C.
    C'est devenu un standard dans tous les langages. On peut remplacer var=var operateur valeur par var operateur= valeur.
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  12. #12
    Invité
    Invité(e)
    Par défaut
    Merci

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 1
    Dernier message: 28/06/2017, 09h36
  2. Exécuter un programme défini par une chaîne de caractère
    Par yop3112 dans le forum Interfaces Graphiques
    Réponses: 6
    Dernier message: 16/06/2015, 14h35
  3. [Python 3.X] Script modifiant une chaîne de caractères dans plusieurs fichiers.
    Par Julien698 dans le forum Général Python
    Réponses: 4
    Dernier message: 09/09/2014, 13h41
  4. modifier une chaîne de caractères dans une fonction void
    Par jujudelyon dans le forum Débuter
    Réponses: 10
    Dernier message: 11/01/2014, 18h47
  5. [Turbo Pascal] Ecrire un programme qui lit une chaîne de caractères
    Par agan2012 dans le forum Turbo Pascal
    Réponses: 9
    Dernier message: 22/02/2009, 20h46

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo