Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Est-ce que le "bricolage" que je fais avec fgets pour me débarasser du 'n' est régulier?

5 réponses
Avatar
bpascal123
Bjr/bsr,

Je fais l'exercice 9.11 de ce tutoriel (chapitre pointeur) :
http://www.ltam.lu/Tutoriel_Ansi_C/

Le d=E9faut (mineur) de ce tutoriel est de faire appel =E0 gets alors
j'essaie de trouver des alternatives ce qui en tant que d=E9butant ne
simplifie pas la progression. Mais je n'abandonne pas.

Alors pour l'exercice qui demande de compter des lettres dans un
phrase voici mon code:

/*
* Exercice 9.11
*
*Ecrire un programme qui lit une cha=EEne de caract=E8res CH au clavier et
qui compte les occurrences des lettres de l'alphabet en ne distinguant
*pas les majuscules et les minuscules. Utiliser un tableau ABC de
dimension 26 pour m=E9moriser le r=E9sultat et un pointeur PCH pour
parcourir *la cha=EEne CH et un pointeur PABC pour parcourir ABC.
Afficher seulement le nombre des lettres qui apparaissent au moins une
fois dans le *texte.
*
*/

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

int main(void)
{
char Abc[26] ; /* stockage alphabet */
char *pAbc ; /* pointeur vers Abc */
int let, cnt, cnt2 ; /* variable d'aide */
char Ch[50] ; /* saisie chaine et transfo en majuscule */
char *pCh ; /* pointeur vers chaine */
char Ch2[50] ; /* copie de Ch1 sans '\n' */
char *pCh2 ; /* pointeur vers Ch1 */

printf("\n\nEntrez une chaine de caracteres : ") ;
fgets(Ch, 50, stdin) ;

printf("\nRemplissage ABC avec lettre cap...") ;
for ( pAbc =3D Abc, let =3D 65 ; pAbc < Abc+26 ; pAbc++, let++ )
*pAbc =3D let ;

printf("\nFrequence des lettres suivantes dans la chaine : \"%s\"",
Abc) ;

/* Manip pour supprimer '\n' dans le buffer lu par fgets */
cnt2 =3D strlen(Ch) ;
cnt2 =3D cnt2 - 1 ;
for ( pCh =3D Ch, pCh2 =3D Ch2 ; pCh < Ch+cnt2 ; pCh++, pCh2++ )
*pCh2 =3D *pCh ;
*pCh2 =3D '\0' ;
*pCh =3D '\0' ;

/* Trsformation en majuscule */
for ( pCh2=3D Ch2 ; *pCh2 ; pCh2++)
*pCh2 =3D toupper(*pCh2) ;

printf("\n\nLa chaine \"%s\" contient : ", Ch) ;
printf("\n") ;

/* Comptage des lettres */

for ( pAbc =3D Abc ; *pAbc ; pAbc++ )
{
cnt =3D 0 ;
for ( pCh2 =3D Ch2 ; *pCh2 ; pCh2++ )
if ( *pAbc =3D=3D *pCh2 )
cnt++ ;
if ( cnt )
printf("\n%d fois la lettre %c", cnt, *pAbc) ;
}

printf("\n") ;

printf("\n%s", Ch) ;

printf("\n\n") ;

return 0 ;
}

Pascal

5 réponses

Avatar
espie
In article ,
wrote:
Bjr/bsr,

Je fais l'exercice 9.11 de ce tutoriel (chapitre pointeur) :
http://www.ltam.lu/Tutoriel_Ansi_C/

Le défaut (mineur) de ce tutoriel est de faire appel à gets alors
j'essaie de trouver des alternatives ce qui en tant que débutant ne
simplifie pas la progression. Mais je n'abandonne pas.



Bon, ta reponse presente quelques defauts concernant les caracteres.
- Premier defaut: codage en dur des caracteres. Au lieu de 65 et 65 + 26,
pourquoi n'utilises-tu pas directement 'A' et 'Z' ?
- Deuxieme defaut: tu supposes que les lettres ont des codes consecutifs.
Si un jour tu tombes sur du EBCDIC, tu risques d'avoir des surprises...

Pour que ca soit "propre", tu devrais initialiser ton tableau de codes de
lettres directement avec les bonnes valeurs.

e.g.,

char maj[26] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
c'est pas vraiment plus long, et ca a l'avantage d'etre plus portable
(et perso, je trouve aussi que c'est plus lisible).

Cote fgets, ta manip pour supprimer 'n' suppose qu'il y a un 'n'.
Il faudrait que tu verifies si ton dernier caractere est bien un 'n'
et que tu le recopies sinon...

Essaie d'entrer une chaine de plus de 50 caracteres, et constate qu'il va
te manquer le dernier.

Cote non souci, mais pas efficace, pourquoi transformes-tu ta chaine en
majuscules, tu pourrais faire ta comparaison directement en utilisant
toupper, ce qui allege un peu.
Avatar
-ed-
On 14 déc, 03:07, ""
wrote:
Bjr/bsr,

Je fais l'exercice 9.11 de ce tutoriel (chapitre pointeur) :http://www.lt am.lu/Tutoriel_Ansi_C/

Le défaut (mineur) de ce tutoriel est de faire appel à gets alors
j'essaie de trouver des alternatives ce qui en tant que débutant ne
simplifie pas la progression. Mais je n'abandonne pas.

Alors pour l'exercice qui demande de compter des lettres dans un
phrase voici mon code:



Mes commentaires (-ed-) :

/*
* Exercice 9.11
*
*Ecrire un programme qui lit une chaîne de caractères CH au clavier et
qui compte les occurrences des lettres de l'alphabet en ne distinguant
*pas les majuscules et les minuscules. Utiliser un tableau ABC de
dimension 26 pour mémoriser le résultat et un pointeur PCH pour
parcourir *la chaîne CH et un pointeur PABC pour parcourir ABC.
Afficher seulement le nombre des lettres qui apparaissent au moins une
fois dans le *texte.
*
*/

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

int main(void)
{
char Abc[26] ; /* stockage alphabet */
/* -ed- OK, mais pourquoi char ? Pour une valeur numérique
"<..> qui compte les occurrences des lettres de l'alphabet",
on utilise plutôt int... D'ailleurs le commentaire est faux
(ce qui explique sans doute ton erreur de type).
Ce n'est pas l'alphabet que tu stockes (quel intérêt ?),
mais les occurrences des lettres saisies.
*/

char *pAbc ; /* pointeur vers Abc */
/* -ed- OK. Penser à adapter le type du pointeur au type des éléments
du tableau. */

int let, cnt, cnt2 ; /* variable d'aide */
/* -ed- voila un commentaire super intéressant ! */


char Ch[50] ; /* saisie chaine et transfo en majuscule */
char *pCh ; /* pointeur vers chaine */
char Ch2[50] ; /* copie de Ch1 sans 'n' */
char *pCh2 ; /* pointeur vers Ch1 */
/* -ed- vers CH2. Mais ce deuxième tableau est sans doute
inutile.
L'usage, comme expliqué 3000 fois, est de remplacer le 'n'
par un 0
si il existe, et ce sans la chaine saisie (inutile de faire
une copie). */

printf("nnEntrez une chaine de caracteres : ") ;
fgets(Ch, 50, stdin) ;
/* -ed- OK, mais sizeof CH est plus 'auto-demerdant' que 50
*/

printf("nRemplissage ABC avec lettre cap...") ;
for ( pAbc = Abc, let = 65 ; pAbc < Abc+26 ; pAbc++, let+
+ )
*pAbc = let ;
/* -ed- mes craintes sont avérées. Tu n'as pas compris
l'énoncé.
C'est le mot 'occurence' que tu ne comprends pas ? */

printf("nFrequence des lettres suivantes dans la chaine : "%s
"",
Abc) ;

/* Manip pour supprimer 'n' dans le buffer lu par fgets */
cnt2 = strlen(Ch) ;
cnt2 = cnt2 - 1 ;
for ( pCh = Ch, pCh2 = Ch2 ; pCh < Ch+cnt2 ; pCh++, pCh2+
+ )
*pCh2 = *pCh ;
*pCh2 = '' ;
*pCh = '' ;

/* -ed- comme je m'en doutais, manip inutilement complexe...


L'usage est le suivant :

Recherche du n :

char *p = strchr (Ch, 'n');

si on l'a trouvé, on le remplace par un 0 :

if (p != NULL)
{
*p = 0;
}

C'est tout. Pas besoin de Ch2.

(on peut éventuellement faire un traitement
-- par exemple, purge de stdin -- si on a pas trouvé le 'n')
*/


/* Trsformation en majuscule */
for ( pCh2= Ch2 ; *pCh2 ; pCh2++)
*pCh2 = toupper(*pCh2) ;

printf("nnLa chaine "%s" contient : ", Ch) ;
printf("n") ;
/* -ed- probablement inutile (on peut faire le traitement 'à
la volée'
sans a voir à modifier la chaine saisie) */

/* Comptage des lettres */
/* -ed- Tout le reste est faux. Le tableau ABC est un tableau
de compteurs
qui doit au préalable avoir été initialisé à 0. Il a une
taille de 26,
qui correspond à chaque lettre de l'alphabet 0=A 1=B ...
Z%.

Il faut donc traiter cheque lettre saisie de la façon
suivante :

1 - la transformer en majuscule
2 - chercher sa position dans un tableau 'alphabet
"ABCD...XYZ".
Ce tableau est une simple chaine de caractères (lecture
seule).
3 - une fois le rang trouvé (0 à 25), on incrémente l'élé ment
de ABC correspondant.

Quand c'est terminé, le tabmleau ABC contient les occurences
des lettres saisies.


*/
for ( pAbc = Abc ; *pAbc ; pAbc++ )
{
cnt = 0 ;
for ( pCh2 = Ch2 ; *pCh2 ; pCh2++ )
if ( *pAbc == *pCh2 )
cnt++ ;
if ( cnt )
printf("n%d fois la lettre %c", cnt, *pAbc) ;
}

printf("n") ;

printf("n%s", Ch) ;

printf("nn") ;

return 0 ;

}

Pose des questions si tu ne comprends pas.
Avatar
Antoine Leca
Marc Espie a écrit :
char maj[26] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
c'est pas vraiment plus long, et ca a l'avantage d'etre plus portable
(et perso, je trouve aussi que c'est plus lisible).



Vu d'ici (Espagne), cela a un autre avantage¹ : on voit immédiatement
qu'il faudrait corriger ce code pour l'adapter aux circonstances !

En effet, en Espagne l'alphabet est différent, et si on peut passer sur
les cas particuliers de CH, LL et autres W, il est indispensable de
rajouter Ñ (entre N et O). Dans d'autres pays, vous auriez d'autres cas,
par exemple Á et autres voyelles longues comptées à part.

Ou meme ¼ en français ; mais me direz-vous, ¼ n'est pas une lettre à
part de l'alphabet : certes, et c'est là un problème du code initial
(qu'il n'est pas facile de résoudre), voire même de l'enoncé: il ne sait
pas traiter les caractères accentués, car il appelle «alphabet» ce qui
est en réalité un jeu (ensemble) de caractères, et devrait donc prendre
en compte plus de lettres que celles de l'alphabet au sens strict.
D'ailleurs Marc a rectifié (peut-être inconsciemment), en appelant «maj»
la variable que Pascal avait nommé «Abc»...


Bref, le code que propose Marc est une bonne habitude à prendre.

Seul détail, je n'aime pas le [26] ; en dehors du fait que cela va faire
tilter le compilo quand on rajoute la Ñ, cela supprime aussi le en
fin de tableau, donc cela peut amener de bêtes débordements de tampon,
en particulier si quelqu'un vient derrière et transforme le code pour
utiliser erronément strchr (à la place de memchr)... et aussi avec le
code de Pascal tel qu'il est, qui utilise un (inexistant) final dans
le tableau parcouru par pAbc pour en signaler la fin et donc sortir de
la boucle for(;*pAbc;)


Antoine
________________
¹: je me doute bien que c'est très probablement ce que Marc entend par
« plus portable », mais c'est pour enfoncer le clou.
Avatar
espie
In article <hg5ae9$uic$,
Antoine Leca wrote:
Marc Espie a écrit :
char maj[26] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
c'est pas vraiment plus long, et ca a l'avantage d'etre plus portable
(et perso, je trouve aussi que c'est plus lisible).



Vu d'ici (Espagne), cela a un autre avantage¹ : on voit immédiatement
qu'il faudrait corriger ce code pour l'adapter aux circonstances !

En effet, en Espagne l'alphabet est différent, et si on peut passer sur
les cas particuliers de CH, LL et autres W, il est indispensable de
rajouter Ñ (entre N et O). Dans d'autres pays, vous auriez d'autres cas,
par exemple Á et autres voyelles longues comptées à part.

Ou meme ¼ en français ; mais me direz-vous, ¼ n'est pas une lettre à
part de l'alphabet : certes, et c'est là un problème du code initial
(qu'il n'est pas facile de résoudre), voire même de l'enoncé: il ne sait
pas traiter les caractères accentués, car il appelle «alphabet» ce qui
est en réalité un jeu (ensemble) de caractères, et devrait donc prendre
en compte plus de lettres que celles de l'alphabet au sens strict.
D'ailleurs Marc a rectifié (peut-être inconsciemment), en appelant «maj»
la variable que Pascal avait nommé «Abc»...




Ouais, enfin bon. Je suis intentionnellement reste TRES loin des problemes
de locale et tout ce qui va avec.

Faut dire que dans toute sa generalite, ca devient tres complexe a gerer.
La, tu supposes que ton Ñ est encode en char... mais en pratique, on se
retrouve vite avec du multi-byte...

En fait, si on suppose qu'on a des char, la bonne technique, ca serait
de parcourir tout l'espace 1 << CHAR_BIT, et d'etiqueter tout ce qui ressemble
de pres ou de loin a une lettre.

En wchar, ca se complique grandement. La solution la plus simple etant
d'avoir une structure dynamique (table de hachage, vraisemblablement), dans
laquelle tu vas inserer les caracteres identifies comme lettres a mesure
que tu les rencontres...



Bref, le code que propose Marc est une bonne habitude à prendre.

Seul détail, je n'aime pas le [26] ; en dehors du fait que cela va faire
tilter le compilo quand on rajoute la Ñ, cela supprime aussi le en
fin de tableau, donc cela peut amener de bêtes débordements de tampon,
en particulier si quelqu'un vient derrière et transforme le code pour
utiliser erronément strchr (à la place de memchr)... et aussi avec le
code de Pascal tel qu'il est, qui utilise un (inexistant) final dans
le tableau parcouru par pAbc pour en signaler la fin et donc sortir de
la boucle for(;*pAbc;)



C'est un tableau, hein, pas une chaine de caracteres. Et le 26 est
intentionnel, ca me permet de m'assurer que j'ai ne rien oublie (de mon
point de vue).

Un des trucs que j'apprend assez vite a mes etudiants, c'est que le C n'en
a rien a foutre des conventions de chaines de caractere, a la base.
A part la construction "bonjour", qui est effectivement un
const char * termine par un '', il n'y a *rien* dans le langage qui
stipule que les chaines de caracteres sont toujours finies par un zero.
C'est une convention, amplement respectee par la bibliotheque
standard, mais qui n'est absolument pas indispensable...

A la limite, je consens a rajouter un commentaire
/* XXX not a C string */
char maj[26] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

mais je me refuse toujours a rendre le code ecrit accessible a des debiles
profonds... le but, c'est que ca soit clair, pas de se proteger du premier
con qui prendra un tableau pour une chaine.


J'ai d'ailleurs un gros programme en prod qui gere ses chaines de
caracteres sous forme d'intervalles, pbegin/pend: le make d'OpenBSD.
Comme on manipule sans arret des fragments de ligne en train d'etre parsees,
utiliser des chaines de caractere "classique" aurait conduit a passer son
temps a recopier des trucs. L'ancienne version etait encore plus drole,
puisqu'elle passait son temps a mettre des 0 a des endroits arbitraires
dans les chaines en train d'etre traitees, puis a remettre le caractere
d'avant au bon endroit. Lorsque je me suis mis en tete d'ameliorer les
perfs et d'essayer de rendre "const correct" une bonne partie du code.
Avatar
Antoine Leca
Marc Espie écrivit :
In article <hg5ae9$uic$,
Antoine Leca wrote:
Marc Espie a écrit :
char maj[26] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";




Ouais, enfin bon. Je suis intentionnellement reste TRES loin des problemes
de locale et tout ce qui va avec.



Oui, et je suis d'accord avec toi sur la complexité que cela génère
(et c'est pourquoi j'avais écrit « qui n'est pas facile de résoudre »).


A la limite, je consens a rajouter un commentaire
/* XXX not a C string */
char maj[26] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";



Je prends !

Je suis d'accord avec ton point de vue, mais je vois tellement de code
qui fais des raccourcis de ce style (je vois "" /donc/ je peux utiliser
les fonctions strxxx()) que je préfère prendre les bretelles en plus de
la ceinture.


Antoine