Comment supprimer les caractères non chiffre en langage C

Résolu/Fermé
salammans Messages postés 1 Date d'inscription mercredi 15 décembre 2021 Statut Membre Dernière intervention 15 décembre 2021 - Modifié le 16 déc. 2021 à 15:18
mamiemando Messages postés 33079 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 23 avril 2024 - 16 déc. 2021 à 16:30
Bonjour,

Je suis débutante en programmation. J'essaie de supprimer les caractères qui ne sont pas des chiffres dans une chaîne (sans utiliser des fonctions déjà existantes).

Le corrigé de mon exercice me montre la solution (voir capture d'écran), mais je ne la comprends pas. Quelqu'un peut m'expliquer?

void supprime_non_chiffres(char* chaine)
{
 int i, j;

 i = 0;
 j = 0;
 while (chaine[i] != '\0')
 {
  if (est_un_chiffre(chaine[i]))
  {
   chaine[j] = chaine[i];
   j++;
  }
  i++;
 }
 chaine[j] = '\0';
}



Merci !
A voir également:

2 réponses

[Dal] Messages postés 6174 Date d'inscription mercredi 15 septembre 2004 Statut Contributeur Dernière intervention 2 février 2024 1 083
Modifié le 16 déc. 2021 à 15:20
Salut salammans,

Qu'est ce que tu ne comprends pas ?

Dans ce que tu envoies, la fonction
est_un_chiffre()
n'est pas implémentée, cependant je suppose qu'elle fonctionne de façon similaire à la fonction standard
isdigit()
.

La fonction
supprime_non_chiffres()
parcourt la chaîne avec deux curseurs :
  • un qui avance sur tous les caractères de la chaîne à la recherche de chiffres
  • l'autre qui avance de un à un seulement lorsqu'un chiffre est trouvé à mesure que les char existants sont écrasés


Lorsque toute la chaîne est parcourue, la chaîne est terminée par le caractère terminateur de chaîne
'\0'
.

Cette fonction
supprime_non_chiffres()
ne peut être appelée qu'avec une chaîne sous la forme de tableau de
char
ou avec un pointeur sur
char
pointant sur de la mémoire allouée avec
malloc
, mais pas avec une constante littérale, puisque la chaîne passée est modifiée sur place.

Dal
0
mamiemando Messages postés 33079 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 23 avril 2024 7 749
Modifié le 22 déc. 2021 à 13:24
Bonjour,

Je me permets de compléter la réponse de Dal

Rappels préliminaires sur les chaînes de caractères et les pointeurs en C
  • chaine
    est un pointeur, c'est à dire l'adresse d'un bloc mémoire.
  • plus précisément,
    chaine
    est de type
    char *
    ce qui signifie que ce bloc mémoire peut être vu comme une tableau dont chaque case fait la taille d'un
    char
    (ça tombe bien, c'est ce qu'on veut pour une chaîne !)
  • comme le pointeur est typé (ici les cases sont vues comme des
    char
    ), alors les opérateurs
    chaine[i]
    ,
    *chaine
    , et
    chaine++
    sont bien définis (ce ne serait pas le cas pour un adresse "générique", qui se note
    void *
    ). Pour ton exercice, seul
    chaine[i]
    nous intéresse. Du point de vue du C, il retourne le
    i
    -ème bloc de taille
    char
    à partir de l'adresse stockée dans la variable
    chaine
    . De notre point de vue d'être humain, cela revient à récupérer le
    i
    -ème caractère de
    chaine
    .
  • cependant, rien dans le type
    char *
    n'indique combien de caractère fait la chaîne. Si on commence à lire caractère par caractère ce bloc, toute la question est donc "quand s'arrêter ?". Pour répondre à ce problème, en C/C++ (et en fait dans tous les langages), en mémoire, une chaîne contient un caractère d'arrêt supplémentaire, par convention
    '\0'
    .
  • cela signifie que la boucle suivante permet d'afficher un par un les caractère de
    s
    :


#include <stdio.h>

int main(){
    const char * s = "Bonjour 123 Au revoir";
    int i;
    while(s[i] != '\0') {
        printf("%c", s[i]);
        i++;
    }
    return 0;
}
  • si on veut ignorer les caractères non numériques, on pourrait utiliser comme le dit Dal
    isdigit
    :


#include <stdio.h>
#include <ctype.h>

int main(){
    const char * s = "Bonjour";
    int i;
    for(i = 0; s[i] != '\0'; i++) {
        if (isdigit(s[i])) {
            printf("%c", s[i]);
        }   
    }
    return 0;
}
  • Note enfin que quand on écrit
    s = "Bonjour"
    , cela signifie implicitement que le bloc
    "Bonjour\0"
    est créé de manière statique (dans l'exécutable). Le
    '\0'
    est implicitement pris en compte.


Que doit faire ton programme ?

Si celle-ci contient des caractères numériques, la chaîne transformée sera plus courte, et c'est pourquoi il faudra :
  • "supprimer" les caractères non-numériques ;
  • "décaler" de n caractères vers la gauche tous les caractères numériques s'ils sont précédés de n caractères non-numériques ;
  • "décaler" de n caractères vers la gauche le caractère d'arrêt
    \0
    .


Que fait ton corrigé ?

Plutôt que de maintenir une variable
n
, ton programme modifie en place la chaîne de caractère. La "tête de lecture" (à la position
i
) est toujours en avance par rapport à la "tête d'écriture" (à la position
j
), donc c'est effectivement une bonne manière de faire.

De plus, comme la chaîne à transformer est déjà allouée en mémoire, il n'est pas utile d'allouer un bloc mémoire pour la chaîne transformée. Nous allons maintenant ajouter quelques
printf
dans le programme pour voir ce qui se passe. Cependant, comme le montre la section suivante, il va y avoir un peu de travail préalable.

Vu que tu n'as pas forcément encore fait le cours sur les allocations mémoires, et même si je prends le soin de tout expliquer, tu peux l'ignorer dans un premier temps si tu as mal à la tête et aller directement à la section "exécution du programme" :-)

Allocations dynamiques,
const char *
, et programme minimal


Cependant il faut savoir que si tu déclare ta chaîne
const char * s = "Bonjour 123 Au revoir";
tu ne pourras pas la modifier (à cause du
const
). On pourrait se dire, pas de problème je vire le
const
. Sauf que c'est une mauvaise idée. En réalité, cette chaîne n'est pas modifiable (elle est stockée dans l'exécutable, pas en mémoire), et donc si tu tentes de la modifier, ton programme plantera à cause d'une erreur de segmentation. Dit autrement, si une chaîne est initialisée en dur dans un programme écrit en C, ce devrait toujours être un
const char *
(chaîne non modifiable).

C'est pourquoi dans le code ci-dessous, je prends le soin d'allouer dynamiquement une copie de
chaine
avec s. Afin de bien séparer ton problème initial et cet aspect là, je sépare maintenant le code en deux : le
main
s'occupe de cette allocation, et la fonction
transform
est une réécriture de ton corrigé :

#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>

void transform(char * s) {
    int i, j = 0;
    for(i = 0; s[i] != '\0'; i++) {
        printf("i = %3d j = %3d s = %s\n", i, j, s);
        if (isdigit(s[i])) {
            s[j] = s[i];
            j++;
        }
    }
    s[j] = '\0';
}


int main(){
    const char * chaine = "Bonjour 123Au456 revoir";
    char * s = (char *) malloc(strlen(chaine) + 1);
    if (s == NULL) {
        fprintf(stderr, "Pas assez de mémoire!");
        return 1;
    }   
    strcpy(s, chaine);
    transform(s);
    printf("s = %s\n", s);
    free(s);
    return 0;
}


Tu noteras que l'allocation mémoire est faite avec
malloc
(qui retourne une adresse générique de type
void *
), donc c'est pour ça qu'on la convertit (cast) en
char *
avec l'opérateur unaire de cast
(char *)
. Ce bloc doit pouvoir contenir autant de caractère que
chaine
, donc la longueur de chaine (qu'on obtient avec la fonction standard
strlen
) + 1 (pour le
'\0'
).

Ensuite, si tu lis la documentation de
malloc
, tu verras que cette fonction retourne
NULL
. Donc ici, on prend le soin de contrôler que l'allocation a fonctionné, et sinon, on écrit un message d'erreur dans la sortie d'erreur standard, puis on quitte le programme avec un code d'exécution non nul pour signifier que le programme a planté.

Si l'allocation a marché, on recopie
chaine
dans
s
à l'aide de
strcpy
, puis on fait la transformation. Ça ne fera pas d'erreur de segmentation car on n'opère pas sur un
const char *
.

Qui dit
malloc
(si celui-ci a fonctionné) dit
free
. Donc, quand on n'a plus besoin du bloc mémoire
s
, on le désalloue avec la fonction
free
. La librairie C sait exactement quel est la taille de ce bloc puisqu'elle en a gardé trace quand tu as fait le
malloc
et c'est pourquoi
free
ne prend en paramètre que l'adresse du bloc à désallouer.

Exécution du programme

On obtient ceci :

(mando@silk) (~) $ gcc -Wall -g toto.c && ./a.out 
i = 0 j = 0 s = Bonjour 123Au456 revoir
i = 1 j = 0 s = Bonjour 123Au456 revoir
i = 2 j = 0 s = Bonjour 123Au456 revoir
i = 3 j = 0 s = Bonjour 123Au456 revoir
i = 4 j = 0 s = Bonjour 123Au456 revoir
i = 5 j = 0 s = Bonjour 123Au456 revoir
i = 6 j = 0 s = Bonjour 123Au456 revoir
i = 7 j = 0 s = Bonjour 123Au456 revoir
i = 8 j = 0 s = Bonjour 123Au456 revoir
i = 9 j = 1 s = 1onjour 123Au456 revoir
i = 10 j = 2 s = 12njour 123Au456 revoir
i = 11 j = 3 s = 123jour 123Au456 revoir
i = 12 j = 3 s = 123jour 123Au456 revoir
i = 13 j = 3 s = 123jour 123Au456 revoir
i = 14 j = 4 s = 1234our 123Au456 revoir
i = 15 j = 5 s = 12345ur 123Au456 revoir
i = 16 j = 6 s = 123456r 123Au456 revoir
i = 17 j = 6 s = 123456r 123Au456 revoir
i = 18 j = 6 s = 123456r 123Au456 revoir
i = 19 j = 6 s = 123456r 123Au456 revoir
i = 20 j = 6 s = 123456r 123Au456 revoir
i = 21 j = 6 s = 123456r 123Au456 revoir
i = 22 j = 6 s = 123456r 123Au456 revoir
s = 123456


Si tu as besoin de plus de clarifications, n'hésite pas.

Bonne chance
0