Comment supprimer les caractères non chiffre en langage C
Résolu
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?

Merci !
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 !
2 réponses
-
Salut salammans,
Qu'est ce que tu ne comprends pas ?
Dans ce que tu envoies, la fonctionest_un_chiffre()
n'est pas implémentée, cependant je suppose qu'elle fonctionne de façon similaire à la fonction standardisdigit()
.
La fonctionsupprime_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 fonctionsupprime_non_chiffres()
ne peut être appelée qu'avec une chaîne sous la forme de tableau dechar
ou avec un pointeur surchar
pointant sur de la mémoire allouée avecmalloc
, mais pas avec une constante littérale, puisque la chaîne passée est modifiée sur place.
Dal -
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 typechar *
ce qui signifie que ce bloc mémoire peut être vu comme une tableau dont chaque case fait la taille d'unchar
(ç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érateurschaine[i]
,*chaine
, etchaine++
sont bien définis (ce ne serait pas le cas pour un adresse "générique", qui se notevoid *
). Pour ton exercice, seulchaine[i]
nous intéresse. Du point de vue du C, il retourne lei
-ème bloc de taillechar
à partir de l'adresse stockée dans la variablechaine
. De notre point de vue d'être humain, cela revient à récupérer lei
-ème caractère dechaine
. - 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 variablen
, ton programme modifie en place la chaîne de caractère. La "tête de lecture" (à la positioni
) est toujours en avance par rapport à la "tête d'écriture" (à la positionj
), 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 quelquesprintf
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îneconst char * s = "Bonjour 123 Au revoir";
tu ne pourras pas la modifier (à cause duconst
). On pourrait se dire, pas de problème je vire leconst
. 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 unconst char *
(chaîne non modifiable).
C'est pourquoi dans le code ci-dessous, je prends le soin d'allouer dynamiquement une copie dechaine
avec s. Afin de bien séparer ton problème initial et cet aspect là, je sépare maintenant le code en deux : lemain
s'occupe de cette allocation, et la fonctiontransform
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 avecmalloc
(qui retourne une adresse générique de typevoid *
), donc c'est pour ça qu'on la convertit (cast) enchar *
avec l'opérateur unaire de cast(char *)
. Ce bloc doit pouvoir contenir autant de caractère quechaine
, donc la longueur de chaine (qu'on obtient avec la fonction standardstrlen
) + 1 (pour le'\0'
).
Ensuite, si tu lis la documentation demalloc
, tu verras que cette fonction retourneNULL
. 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 recopiechaine
danss
à l'aide destrcpy
, puis on fait la transformation. Ça ne fera pas d'erreur de segmentation car on n'opère pas sur unconst char *
.
Qui ditmalloc
(si celui-ci a fonctionné) ditfree
. Donc, quand on n'a plus besoin du bloc mémoires
, on le désalloue avec la fonctionfree
. La librairie C sait exactement quel est la taille de ce bloc puisqu'elle en a gardé trace quand tu as fait lemalloc
et c'est pourquoifree
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 -