Pour résoudre cet exercice, on peut commencer par traiter les nombres à 1 chiffre, 2 chiffres, 3 chiffres et enfin étendre le principe aux nombres à n chiffres.
Les nombres à 1 et 2 chiffres
Pour les nombres à 1 chiffre, normalement pas de problèmes.
On va créer une fonction qui prend 2 paramètres : nombre1Chiffre(intn,char*dst);
Ici on va simplement initialiser un tableau de 10 éléments, chaque élément correspondant à la traduction en lettres de son index.
tab[0]="zero"tab[1]="un"
On rentre dans le vif du sujet avec les
Nombres à 2 chiffres
Prenons un cas général, 23 par exemple. Naturellement pour avoir sa traduction en lettre, on le lit de gauche à droite et on prononce "vingt trois". C'est un nombre à 2 chiffres, donc le chiffre de gauche représente les dizaines tandis que celui de droite représente les unités.
Maintenant, comment décomposer ce nombre en 2 parties ?
Le nombre 23 peut s'écrire : <math>\(2 * 10 + 3\)</math>
Si on généralise, un nombre à 2 chiffres <math>\(c_1c_2\)</math> peut s'écrire : <math>\(c_1 * 10 + c_2\)</math>
Pour obtenir <math>\(c_1\)</math> et <math>\(c_2\)</math>, on divise notre nombre <math>\(c_1c_2\)</math> par 10. Le quotient de la division entière sera <math>\(c_1\)</math>, et le reste <math>\(c_2\)</math>.
Si le quotient est nul -> pas de dizaines,
si le reste est nul pas d'unités(on n'écrit pas dix zéro... enfin, si parfois dans le cas d'un match de foot)
En suivant le même principe que pour les nombres à 1 chiffre on va écrire la fonction nombre2chiffres(intn,char*dst);
void nombre2chiffres(int n, char *dst)
{
char *s_dizaines[] =
{
"dix", "vingt", "trente", "quarante", "cinquante", "soixante", "quatre-vingt",
};
/* Quotient de la division n / 10 */
int q = n / 10;
/* Reste de la division n / 10 */
int r = n % 10;
if (q > 0)
{
/* Le quotient n'est pas nul, on n'est bien en présence d'un nombre à
* 2 chiffres, on concatène les dizaines.
* Ici, l'index 0 correspond à 10, on doit dont soustraire 1 à q pour
* obtenir le bon index de tableau
*/
strcat(s, s_dizaines[q - 1]);
if (r > 0)
{
/* Le reste n'est pas nul on concatène les unités */
nombre1chiffre(s, r);
}
}
else
{
/* Le quotient est nul, notre nombre n'a qu'un chiffre */
nombre1chiffre(s, r);
}
}
23 -> vingttrois
Process returned 0 (0x0) execution time : 0.000 s
Press any key to continue.
Premier constat, le principe fonctionne... Presque.
Il n'y a pas de séparations entre les 2 chiffres, et si vous testez 11, vous obtenez
11 -> dixun
De plus cela ne fonctionne pas pour les nombres supérieurs à 70(essayez si vous ne me croyez pas ...).
Pas de panique, un problème après l'autre!...
Tout d'abord, le problème des nombres dans l'intervalle [10, 19].
On va simplement modifier la fonction pour traiter séparément
les nombres dans l'intervalle <math>\([10, 19]\)</math>
les nombres dans l'intervalle<math>\([0, 9] U [20, 99]\)</math>
Notre fonction devient
void nombre2chiffres(int n, char *dst)
{
char *s_dizaines[] =
{
"dix", "vingt", "trente", "quarante", "cinquante",
"soixante", "quatre-vingt",
};
/* Nombre entre 11 et 19 */
char *s_pdizaines[] =
{
"dix", "onze", "douze", "treize", "quatorze",
"quinze", "seize", "dix-sept", "dix-huit", "dix-neuf",
};
/* Quotient de la division n / 10 */
int q = n / 10;
/* Reste de la division n / 10 */
int r = n % 10;
if (n >= 10 && n <= 19)
{
/* On est dans l'intervalle [10, 19]
* r contient correspond à l'index de la traduction
*/
strcat(dst, s_pdizaines[r]);
}
else
{
/* On est dans l'intervalle [0, 9] U [20, 99] */
if (q == 0)
{
/* Le quotient est nul.
* Notre nombre à 2 chiffres est en fait un nombre à 1 chiffre
*/
nombre1chiffre(r, dst);
}
else
{
/* Le qutient n'est pas nul, on n'est bien en présence d'un nombre
* à 2 chiffres, on concatène les dizaines.
* Ici, l'index 0 correpond à 11, on doit dont soustraire 1 à q pour
* obtenir le bon index de tableau
*/
strcat(dst, s_dizaines[q - 1]);
if (r != 0)
{
/* Le reste n'est pas nul on concatène les unités */
nombre1chiffre(r, dst);
}
}
}
}
Et on obtient bien
11 -> onze
Et c'est ok jusqu'à 69.
Problème suivant, les nombres dans l'intervalle [70, 79] U [90, 99].
Les nombres de cet ensemble, s'écrivent avec la dizaine précédente auquel on ajoute un nombre dans l'intervalle [10, 19].
73 -> soixante treize
92 -> quatre-vingt douze
On va commencer par faire correspondre l'indice 7 à la chaîne "soixante" et l'indice 9 à la chaîne "quatre-vingt".
ensuite il suffit de tester si le quotient est égal à 7 ou 9. Si c'est le cas, on affiche notre dizaine normalement, mais on ajoutera 10 au reste pour avoir un nombre dans l'intervalle[10, 19]
On obtient
void nombre2chiffres(int n, char *dst)
{
char *s_dizaines[] =
{
"vingt", "trente", "quarante", "cinquante",
"soixante", "soixante", "quatre-vingt", "quatre-vingt"
};
/* Nombre entre 11 et 19 */
char *s_pdizaines[] =
{
"dix", "onze", "douze", "treize", "quatorze",
"quinze", "seize", "dix-sept", "dix-huit", "dix-neuf",
};
/* Quotient de la division n / 10 */
int q = n / 10;
/* Reste de la division n / 10 */
int r = n % 10;
if (n >= 10 && n <= 19)
{
/* On est dans l'intervalle [10, 19]
* r contient correspond à l'index de la traduction
*/
strcat(dst, s_pdizaines[r]);
}
else
{
/* On est dans l'interevalle [0, 9] U [20, 99] */
if (q == 0)
{
/* Le quotient est nul.
* Notre nombre à 2 chiffres est en fait un nombre à 1 chiffre
*/
nombre1chiffre(r, dst);
}
else
{
/* Le quotient n'est pas nul, on n'est bien en présence d'un nombre
* à 2 chiffres, on concatène les dizaines.
* Ici, l'index 0 correpond à 20, on doit dont soustraire 2 à q pour
* obtenir le bon index de tableau
*/
strcat(dst, s_dizaines[q - 2]);
if (q == 7 || q == 9)
{
/* On est dans l'intervalle [70, 79] U [90, 99]
* On ajoute 10 au reste.
*/
r += 10;
}
if (r != 0)
{
/* Le reste n'est pas nul on concatène le chiffre de gauche */
if (r >= 10)
{
/* On est dans l'intervalle [70, 79] U [90, 99] */
strcat(dst, s_pdizaines[r - 10]);
}
else
{
/* Cas général */
nombre1chiffre(r, dst);
}
}
}
}
}
Il nous reste à séparer nos 2 chiffres en se rappelant que
Citation : Le conjugueur
Prennent un trait d'union tous les nombres composés inférieurs à 100 ne se terminant pas en 1 sauf 81 et 91 :
Reste également à faire attention au seul cas particulier restant qui concerne l'écriture des nombre à 2 chiffres :
Citation : Le conjugueur
20 et 100 s'accordent quand ils sont multipliés par un nombre sans être suivis par un autre nombre.
quatre-vingts
quatre-vingt-un
void nombre2chiffres(int n, char *dst)
{
char *s_dizaines[] =
{
"vingt", "trente", "quarante", "cinquante",
"soixante", "soixante", "quatre-vingt", "quatre-vingt"
};
/* Nombre entre 11 et 19 */
char *s_pdizaines[] =
{
"dix", "onze", "douze", "treize", "quatorze",
"quinze", "seize", "dix-sept", "dix-huit", "dix-neuf",
};
/* Quotient de la division n / 10 */
int q = n / 10;
/* Reste de la division n / 10 */
int r = n % 10;
if (n >= 10 && n <= 19)
{
/* On est dans l'intervalle [10, 19]
* r contient correspond à l'index de la traduction
*/
strcat(dst, s_pdizaines[r]);
}
else
{
/* On est dans l'interevalle [0, 9] U [20, 99] */
if (q == 0)
{
/* L' quotient est nul.
* Notre nombre à 2 chiffres est en fait un nombre à 1 chiffre
*/
nombre1chiffre(r, dst);
}
else
{
/* Le quotient n'est pas nul, on n'est bien en présence d'un nombre
* à 2 chiffres, on concatène les dizaines.
* Ici, l'index 0 correspond à 20, on doit dont soustraire 2 à q pour
* obtenir le bon index de tableau
*/
strcat(dst, s_dizaines[q - 2]);
if (q == 7 || q == 9)
{
/* On est dans l'intervalle [70, 79] U [90, 99]
* On ajoute 10 au reste.
*/
r += 10;
}
if (q == 8 && r == 0)/* if (n == 80) */
{
/* On ajoute le s à quatre-vingts */
strcat(dst, "s");
}
if (r != 0)
{
/* Le reste n'est pas nul, on ajoute le séparateur approprié*/
if ((r == 1 || r == 11)
{
if (q != 8 && q != 9)
{
/* ... et un ou ... et onze */
strcat(dst, " et ");
}
}
else
{
/* Dans tous les autres cas, c'est un tiret */
strcat(dst, "-");
}
if (r >= 10)
{
/* On est dans l'intervalle [70, 79] U [90, 99] */
strcat(dst, s_pdizaines[r - 10]);
}
else
{
/* Cas général */
nombre1chiffre(r, dst);
}
}
}
}
}
Un exemple 123. Un nombre à 3 chiffres est... une centaine suivie d'un nombre à 2 chiffres.
Pour isoler cette centaine, même principe que pour un nombre à 2 chiffres, on utilise la division entière :
123 / 100 = 1
123 % 100 = 23
Le seul cas particulier à prendre en compte est l'accord de cent.
Citation : Le conjugueur
20 et 100 s'accordent quand ils sont multipliés par un nombre sans être suivis par un autre nombre.
C'est tout.
On obtient(je donne les 3 fonctions, plus un main de test)
#include <stdio.h>
void nombre1chiffre(int n, char *dst)
{
char *s_chiffres[] =
{
"zero", "un", "deux", "trois", "quatre",
"cinq", "six", "sept", "huit", "neuf",
};
strcat(dst, s_chiffres[n]);
}
void nombre2chiffres(int n, char *dst)
{
char *s_dizaines[] =
{
"vingt", "trente", "quarante", "cinquante",
"soixante", "soixante", "quatre-vingt", "quatre-vingt"
};
/* Nombre entre 11 et 19 */
char *s_pdizaines[] =
{
"dix", "onze", "douze", "treize", "quatorze",
"quinze", "seize", "dix-sept", "dix-huit", "dix-neuf",
};
/* Quotient de la division n / 10 */
int q = n / 10;
/* Reste de la division n / 10 */
int r = n % 10;
if (n >= 10 && n <= 19)
{
/* On est dans l'intervalle [10, 19]
* r contient correspond à l'index de la traduction
*/
strcat(dst, s_pdizaines[r]);
}
else
{
/* On est dans l'interevalle [0, 9] U [20, 99] */
if (q == 0)
{
/* Le quotient est nul.
* Notre nombre à 2 chiffres est en fait un nombre à 1 chiffre
*/
nombre1chiffre(r, dst);
}
else
{
/* Le quotient n'est pas nul, on n'est bien en présence d'un nombre
* à 2 chiffres, on concatène les dizaines.
* Ici, l'index 0 correpond à 20, on doit dont soustraire 2 à q pour
* obtenir le bon index de tableau
*/
strcat(dst, s_dizaines[q - 2]);
if (q == 7 || q == 9)
{
/* On est dans l'intervalle [70, 79] U [90, 99]
* On ajoute 10 au reste.
*/
r += 10;
}
if (q == 8 && r == 0)
{
/* On ajoute le s à quatre-vingts */
strcat(dst, "s");
}
if (r != 0)
{
/* Le reste n'est pas nul, on ajoute le séparateur approprié*/
if ((r == 1 || r == 11) && q != 8 && q != 9)
{
/* ... et un ou ... et onze */
strcat(dst, " et ");
}
else
{
/* Dans tous les autres cas, c'est un tiret */
strcat(dst, "-");
}
if (r >= 10)
{
/* On est dans l'intervalle [70, 79] U [90, 99] */
strcat(dst, s_pdizaines[r - 10]);
}
else
{
/* Cas général */
nombre1chiffre(r, dst);
}
}
}
}
}
void nombre3chiffres(int n, char *dst)
{
/* Quotient de la division n / 100 */
int q = n / 100;
/* Reste de la division n / 100 */
int r = n % 100;
if (q == 0)
{
/* Pas de centaines, c'est un nombre à 2 chiffres */
nombre2chiffres(n, dst);
}
else
{
/* Plusieurs centaines */
if (q > 1)
{
/* On ajoute le nombre de centaines */
nombre2chiffres(q, dst);
/* Un espace */
strcat(dst, " ");
}
/* Le mot cent */
strcat(dst, "cent");
if (q > 1 && r == 0)
{
/* Un s si besoin */
strcat(dst, "s");
}
}
if (r > 0)
{
/* il y a un reste */
/* On ajoute un espace, suivi des dizaines */
strcat(dst, " ");
nombre2chiffres(r, dst);
}
}
int main(void)
{
int i;
for (i = 0; i < 1000; ++i)
{
char res[256] = "";
nombre3chiffres(i, res);
printf("%d -> %s\n", i, res);
}
return 0;
}
Facile, non?
Il ne reste plus qu'à nous occuper des
Nombres à n chiffres
(avec n <= 12)
Le prototype donné pour l'exercice était char*z0zero(char*src,char*dst);
La principale différence, par rapport aux fonctions précédentes, c'est qu'ici, le nombre est representé sous forme d'une chaîne de caractères.
Prenons un exemple: "2256":
Notre nombre est un nombre de milliers(2), suivi d'un nombre à 3 chiffres(256).
2256 = 2 * 1000 + 256
Même principe qu'auparavant, on doit séparer notre nombre en 2 parties, les milliers partie gauche, et les centaines, parties droite. Pour un nombre entre 1000 et 999999 :
milliers = nombre / 1000
centaines = nombre % 1000
Pour un nombre >= 1000000
exemple: "1256512"
1256512 = 1 * 1000000 + 256 * 1000 + 512
millions = nombre / 1000000
milliers = nombre % 1000000
Seulement on travaille sur une chaîne de caractères! Comment faire une division sur une chaîne de caractères?
Tout d'abord, comment connaitre le nombre de chiffres de notre nombre ? strlen fera très bien l'affaire.
strlen("2256") -> 4, et on obtient bien le nombre de chiffres.
Pour isoler la partie gauche. On divise le nombre de chiffres par 3. 3, car chaque groupe de 3 chiffres isole une puissance de 1000 <math>\(1 000 = 10^3\)</math> <math>\(1 000 000 = 10^6\)</math>
Ainsi, si le nombre de chiffres / 3 est
= 0 on est dans le groupe des centaines
= 1 on est dans le groupe des milliers
= 2 on est dans le groupe des millions
= 3 on est dans le groupe des milliards
Si le reste de la division est nul, le nombre appartient au groupe précédent.
Un exemple:
999 -> nombre de chiffres 3
3 / 3 ->
quotient = 1,
reste = 0,
le reste est nul donc :
groupe = quotient - 1 -> groupe = 0
999 appartient au groupe des centaines.
Pour obtenir le nombre de chiffres du quotient, c'est simple, on multiplie la valeur du groupe par 3 et on soustrait le résultat au nombre de chiffres total.
nombre de chiffre du quotient = nombre de chiffres - groupe * 3
Ce sera surement plus clair avec un exemple:
Toujours avec 2256 :
2256
nombre de chiffres = 4
2256 / 3 = 1 et le reste n'est pas nul, donc on est dans le groupe des milliers.
4 - 1 * 3 = 1
La partie gauche a 1 chiffre -> 2256
On a la méthode, reste à la coder.
Les 3 fonctions précédentes ne travaillent pas sur une chaîne de caractères, mais sur un entier.
Il va donc falloir convertir la partie gauche de notre nombre en entier.
Pour ce faire on copie cette partie gauche dans une chaîne temporaire. La partie à convertir à forcément 3 chiffres, donc une taille de 4 caractère(3 chiffres + le '\0'), est suffisante.
Pendant qu'on y est, on convertit également la partie droite du nombre. Ici, je n'utilise pas de variable temporaire, car la partie droite du nombre est terminée par '\0', on est bien en présence d'une chaîne valide.
Pour mieux, comprendre, voici la représentation en mémoire de notre chaîne :
src + 0
src + 1
src + 2
src + 3
src + 4
'1'
'2'
'3'
'4'
'\0'
La partie gauche de notre nombre débute à src + 0(src) et à un taille de 1 caractère.
La partie droite de notre nombre débute à src + 1 et se termine au caractère '\0'.
Pour la copie, j'ai utilisé la fonction char*strncpy(char*dest,constchar*src,size_tn);
Citation : <man>
La fonction strncpy() est identique, sauf que seuls les n premiers octets de src sont copiés. Ainsi, s'il n'y a pas de caractère nul dans les n premiers octets de src, la chaîne résultante ne disposera de caractère nul final. Dans le cas où la longueur src est inférieure à n, la fin de dest sera remplie avec des caractères nuls.
Pour la conversion, j'utilise la fonction longintstrtol(constchar*nptr,char**endptr,intbase);
Citation : <man>
La fonction strtol() convertit la chaîne nptr en un entier long en fonction de l'argument base, qui doit être dans l'intervalle 2 a 36 (bornes comprises), ou avoir la valeur spéciale 0.
.
On a traiteé le cas général, maintenant au tour des cas particuliers
Citation : Le conjugueur
Mille est toujours invariable
* trois mille * dix mille deux
Citation : Le conjugueur
million et milliard sont des noms et non des adjectifs. Ils ne font pas vraiment partie du nombre et laissent place à l'accord :
* quatre cents millions
* deux cent mille
* deux cents milliers
Et enfin le troisième cas particulier,
Citation
1000 tout comme 100, ne s'écrit pas un mille, mais bel et bien mille!
On va devoir modifier nos premières fonctions pour gérer ses exeptions. En effet,
80000 -> quatre-vingts mille.
1000 -> un mille
On va simplement passer le groupe courant au fonctions qui en ont besoin
Leur prototype devient
void nombre2chiffres(char *s, int n, int p);
void nombre3chiffres(char *s, int n, int p);
Apparait également la fonction char*sauterZeros(char*s);
qui sert commer son nom l'indique, à sauter les zéros inutiles en début de nombre :
00001 -> 1
01256 -> 1256
Au final, le programme complet
#include <stdio.h>
#include <string.h> /* pour strlen, strcat, strncpy */
#include <stdlib.h> /* pour strtol() */
char *sauterZeros(char *s)
{
/* Tant que le caractère actuel est '0' */
while (*s == '0')
{
/* On avance dans la chaîne */
++s;
}
return s;
}
void nombre1chiffre(int n, char *dst)
{
char *s_chiffres[] =
{
"zero", "un", "deux", "trois", "quatre",
"cinq", "six", "sept", "huit", "neuf",
};
strcat(dst, s_chiffres[n]);
}
void nombre2chiffres(int n, char *dst, int p)
{
char *s_dizaines[] =
{
"vingt", "trente", "quarante", "cinquante",
"soixante", "soixante", "quatre-vingt", "quatre-vingt"
};
/* Nombre entre 11 et 19 */
char *s_pdizaines[] =
{
"dix", "onze", "douze", "treize", "quatorze",
"quinze", "seize", "dix-sept", "dix-huit", "dix-neuf",
};
/* Quotient de la division n / 10 */
int q = n / 10;
/* Reste de la division n / 10 */
int r = n % 10;
if (n >= 10 && n <= 19)
{
/* On est dans l'intervalle [10, 19]
* r contient correspond à l'index de la traduction
*/
strcat(dst, s_pdizaines[r]);
}
else
{
/* On est dans l'interevalle [0, 9] U [20, 99] */
if (q == 0)
{
/* Le quotient est nul.
* Notre nombre à 2 chiffres est en fait un nombre à 1 chiffre
*/
nombre1chiffre(r, dst);
}
else
{
/* Le quotient n'est pas nul, on n'est bien en présence d'un nombre
* à 2 chiffres, on concatène les dizaines.
* Ici, l'index 0 correpond à 20, on doit dont soustraire 2 à q pour
* obtenir le bon index de tableau
*/
strcat(dst, s_dizaines[q - 2]);
if (q == 7 || q == 9)
{
/* On est dans l'intervalle [70, 79] U [90, 99]
* On ajoute 10 au reste.
*/
r += 10;
}
if (q == 8 && r == 0)
{
/* On ajoute le s à quatre-vingts si on ne se trouve pas dans
* les milliers.
* On n'écris pas quatre-vingts-mille */
if (p != 1)
{
strcat(dst, "s");
}
}
if (r != 0)
{
/* Le reste n'est pas nul, on ajoute le séparateur approprié*/
if ((r == 1 || r == 11) && q != 8 && q != 9)
{
/* ... et un ou ... et onze */
strcat(dst, " et ");
}
else
{
/* Dans tous les autres cas, c'est un tiret */
strcat(dst, "-");
}
if (r >= 10)
{
/* On est dans l'intervalle [70, 79] U [90, 99] */
strcat(dst, s_pdizaines[r - 10]);
}
else
{
/* Cas général */
nombre1chiffre(r, dst);
}
}
}
}
}
void nombre3chiffres(int n, char *dst, int p)
{
/* Quotient de la division n / 100 */
int q = n / 100;
/* Reste de la division n / 100 */
int r = n % 100;
if (q == 0)
{
/* Pas de centaines, c'est un nombre à 2 chiffres */
/* On prend garde à ne pas écrire un mille */
if (p != 1 || n > 1)
{
nombre2chiffres(n, dst, p);
}
}
else
{
/* Plusieurs centaines */
if (q > 1)
{
/* On ajoute le nombre de centaines */
nombre2chiffres(q, dst, p);
/* Un espace */
strcat(dst, " ");
}
/* Le mot cent */
strcat(dst, "cent");
/* Un s si besoin */
if (q > 1 && r == 0 && p != 1)
{
strcat(dst, "s");
}
if (r > 0)
{
/* il y a un reste */
/* On ajoute un espace, suivi des dizaines */
strcat(dst, " ");
nombre2chiffres(r, dst, p);
}
}
}
char *z0zero(char *src, char *dst)
{
char const *s_p3[] =
{
"mille", "million", "milliard"
};
int n;
/* On s'assure que la chaîne des destination débute bien par '\0' */
dst[0] = '\0';
/* On saute les zéros inutiles à gauche du nombre */
src = sauterZeros(src);
/* Nombre de chiffre de la chaîne source */
n = strlen(src);
if (n == 0)
{
/* cas particulier du zéro. */
nombre1chiffre(0, dst);
}
else
{
int q, r;
/* Tant que le nombre actuel à plus de trois chiffres */
while (n > 3)
{
/* Nombre de chiffres de la partie gauche */
int nq;
/* Groupe courant */
int p = n / 3;
/* Chaîne temporaire pour la conversion de la partie gauche
* en int */
char s_q[4] = {0};
if (n % 3 == 0)
p--;
nq = n - p * 3;
/* On copie le groupe courant dans une chaîne temporaire */
strncpy(s_q, src, nq);
s_q[nq] = '\0';
/* On convertit le groupe courant en entier */
q = strtol(s_q, NULL, 10);
/* On convertit la partie droite du nombre en entier */
r = strtol(src + nq, NULL, 10);
/* On traduit le groupe courant en mot */
nombre3chiffres(q, dst, p);
if (q > 1 || p != 1)
{
/* On ajoute un espace si le on a terminé le mot pas un s */
strcat(dst, " ");
}
/* On ajoute la traduction de la puissance de 1000 correspondant
* au groupe actuel
*/
strcat(dst, s_p3[p - 1]);
if (p != 1 && q > 1)
{
/* Mille est invariable, millions et milliards, non */
strcat (dst, "s");
}
if (r != 0)
{
/* La traduction n'est pas terminée, on ajoute un espace */
strcat(dst, " ");
}
/* On saute les zéros a gauche du reste.
* Le reste devient le nouveau nombre à traduire.
*/
src = sauterZeros(src + nq);
/* Nouvelle longueur de la chaîne */
n = strlen(src);
}
if (n > 0)
{
/* On termine par le groupe des centaines */
nombre3chiffres(strtol(src, NULL, 10), dst, 0);
}
}
return dst;
}
/* Le main de test de candide */
int main (void)
{
char *x[] =
{ "0", "1", "2", "3", "4", "5", "7", "8", "9", "10", "11", "12", "13",
"14", "15", "16", "17", "18", "19", "20", "21", "29", "30", "40", "42",
"51",
"60", "69", "70", "71", "77", "80", "81", "85", "90", "91", "99", "100",
"101",
"110", "130", "173", "196", "200", "205", "217", "223", "256", "268",
"273",
"280", "297", "734", "1000", "1001", "1017", "1020", "1032", "1111",
"2000",
"2100", "2200", "3003", "3780", "10000", "10005", "10900", "11000",
"11700",
"12000", "18805", "61400", "77010", "80000", "80078", "90000", "92002",
"97459",
"100000", "100007", "204003", "310857", "700005", "1000000", "2000000",
"2001001",
"72424600", "80000000", "79828480", "200000000", "708000000",
"1000000000", "2000000000",
"2000500000", "2000780001", "111111111111", "990990990990"
};
int i, n = sizeof x / sizeof *x;
for (i = 0; i < n; i++)
{
char NombreEnLettres[256] = {0};
z0zero(x[i], NombreEnLettres);
if (NombreEnLettres != NULL)
printf("%s.\n", NombreEnLettres);
}
return 0;
}
Le même sans commentaires :
#include <stdio.h>
#include <string.h> /* pour strlen, strcat, strncpy */
#include <stdlib.h> /* pour strtol() */
char *sauterZeros(char *s)
{
while (*s == '0')
++s;
return s;
}
void nombre1chiffre(int n, char *dst)
{
char *s_chiffres[] =
{
"zero", "un", "deux", "trois", "quatre",
"cinq", "six", "sept", "huit", "neuf",
};
strcat(dst, s_chiffres[n]);
}
void nombre2chiffres(int n, char *dst, int p)
{
char *s_dizaines[] =
{
"vingt", "trente", "quarante", "cinquante",
"soixante", "soixante", "quatre-vingt", "quatre-vingt"
};
/* Nombre entre 11 et 19 */
char *s_pdizaines[] =
{
"dix", "onze", "douze", "treize", "quatorze",
"quinze", "seize", "dix-sept", "dix-huit", "dix-neuf",
};
int q = n / 10;
int r = n % 10;
if (n >= 10 && n <= 19)
strcat(dst, s_pdizaines[r]);
else
{
if (q == 0)
nombre1chiffre(r, dst);
else
{
strcat(dst, s_dizaines[q - 2]);
if (q == 7 || q == 9)
r += 10;
if (q == 8 && r == 0)
if (p != 1)
strcat(dst, "s");
if (r != 0)
{
if ((r == 1 || r == 11) && q != 8 && q != 9)
strcat(dst, " et ");
else
strcat(dst, "-");
if (r >= 10)
strcat(dst, s_pdizaines[r - 10]);
else
nombre1chiffre(r, dst);
}
}
}
}
void nombre3chiffres(int n, char *dst, int p)
{
int q = n / 100;
int r = n % 100;
if (q == 0)
{
if (p != 1 || n > 1)
nombre2chiffres(n, dst, p);
}
else
{
if (q > 1)
{
nombre2chiffres(q, dst, p);
strcat(dst, " ");
}
strcat(dst, "cent");
if (q > 1 && r == 0 && p != 1)
strcat(dst, "s");
if (r > 0)
{
strcat(dst, " ");
nombre2chiffres(r, dst, p);
}
}
}
char *z0zero(char *src, char *dst)
{
char const *s_p3[] =
{
"mille", "million", "milliard"
};
int n;
dst[0] = '\0';
src = sauterZeros(src);
n = strlen(src);
if (n == 0)
nombre1chiffre(0, dst);
else
{
int q, r;
while (n > 3)
{
int nq;
int p = n / 3;
char s_q[4] = {0};
if (n % 3 == 0)
p--;
nq = n - p * 3;
strncpy(s_q, src, nq);
s_q[nq] = '\0';
q = strtol(s_q, NULL, 10);
r = strtol(src + nq, NULL, 10);
nombre3chiffres(q, dst, p);
if (q > 1 || p != 1)
strcat(dst, " ");
strcat(dst, s_p3[p - 1]);
if (p != 1 && q > 1)
strcat (dst, "s");
if (r != 0)
strcat(dst, " ");
src = sauterZeros(src + nq);
n = strlen(src);
}
if (n > 0)
nombre3chiffres(strtol(src, NULL, 10), dst, 0);
}
return dst;
}
/* Le main de test de candide */
int main (void)
{
char *x[] =
{ "0", "1", "2", "3", "4", "5", "7", "8", "9", "10", "11", "12", "13",
"14", "15", "16", "17", "18", "19", "20", "21", "29", "30", "40", "42",
"51",
"60", "69", "70", "71", "77", "80", "81", "85", "90", "91", "99", "100",
"101",
"110", "130", "173", "196", "200", "205", "217", "223", "256", "268",
"273",
"280", "297", "734", "1000", "1001", "1017", "1020", "1032", "1111",
"2000",
"2100", "2200", "3003", "3780", "10000", "10005", "10900", "11000",
"11700",
"12000", "18805", "61400", "77010", "80000", "80078", "90000", "92002",
"97459",
"100000", "100007", "204003", "310857", "700005", "1000000", "2000000",
"2001001",
"72424600", "80000000", "79828480", "200000000", "708000000",
"1000000000", "2000000000",
"2000500000", "2000780001", "111111111111", "990990990990"
};
int i, n = sizeof x / sizeof *x;
for (i = 0; i < n; i++)
{
char NombreEnLettres[256] = {0};
z0zero(x[i], NombreEnLettres);
if (NombreEnLettres != NULL)
printf("%s.\n", NombreEnLettres);
}
return 0;
}
Bilan
Cet exercice fait manipuler quelques fonctions standards(strlen,strncpy,strcat,strtol), des oprérations arithmétiques(division et modulo), des opérateurs logiques(&&,||,!), des chaînes de caractères. Finalement que des notions abordées dans le tuto de Mathéo21.
Il reste pourtant difficile, surtout à cause des multiples exceptions de la langue française concernant l'écriture des nombres.
Une grande partie de cet exercice, ne se résoud pas devant l'écran, mais avec un papier et un crayon, en essayant des exemples à la main.
Si on se limite aux nombres à 2 chiffres, voire 3, il est très accessible.
Un conseil, ne vous limitez pas à cette correction, et regardez les différentes solutions proposées.
Comme déjà dit, le prochain exercice sera beaucoup plus simple.
Ça me paraît bon, il y a juste une coquille pour 17, 18 et 19.
Sinon, ta correction me paraît excellente, en particulier tes fonctions s'enchaînent comme lorsqu'on fait la traduction informelle dans la vie courante.
Perso, j'aurais juste créé une fonction de découpage du nombre en tranches de trois chiffres à partir de la droite mais ça peut se discuter.
Salut, je sais que j'ai jamais vraiment participer ici, car le dernier exo me parraissait un peu trop dur , mais à quand le prochain exo (j'ai entendu qu'il sera plus simple )
Demande l'autorisation de mettre les tutos de se topic (au norme avec les cours du SDZ) sur mon site pour faire un peu d'ordre sur le SDZ
Car il me semble mieux adapté à cette situation si possible que chaque nouvelle exercice soit annoncé sur mon site et que se topic soit bloqué après avoir mit l'adresse de mon site en première page de se même topic
Titre : zSommeChiffres Mois : Second exercice de janvier 2010(durée 15 jours.) Sujet : Calculer la somme des chiffres d'un nombre donné. Compter le nombre de fois où une somme est présente dans un intervalle donné. Connaissances requises : Partie 1 : [Théorie] Les bases du débutant
Objectif
Pour tout nombre entier(dans sa représentation décimale), on peut faire la somme de ses chiffres, par exemple :
92538
9 + 2 + 5 + 3 + 8
92538 -> 27
L'exercice consiste à écrire La fonction intzSommeChiffres(intn);
qui retourne la somme des chiffres d'un entier passé en paramètre.
Je vous propose le template suivante:
#include <stdio.h>
int zSommeChiffres(int n)
{
...
return somme;
}
int main(void)
{
int n1 = 92538;
printf("%d -> %d\n", n1 , zSommeChiffres(n1));
return 0;
}
Pour ceux qui sont vraiment allergiques aux fonctions, rien ne vous empêche de faire cet exercice en mettant le code dans la fonction main.
Connaissances requises : Les chaînes de caractères
Votre programme devra réaliser la même chose, mais les nombres seront représentés sous forme de chaine de caractères. Ainsi, votre programme devra être capable de faire la somme des chiffres du nombre :
Combien de nombres(en représentation décimale) entre 0 et N ont la somme de leurs chiffres égale à S.
Par exemple entre 0 et 1000000000, combien de nombres ont la somme de leurs chiffres égale à 27 ?
Le problème pourra être résolu avec l'une ou l'autre des fonctions(ou avec les 2 ).
@Gurney : Dans ta solution pense bien à rajouter le '\0' final après avoir utilisé strncpy. Car elle ne le rajoute pas automatiquement comme le dit le manuel.
Citation
La fonction strncpy() est identique, sauf que seuls les n premiers octets de src sont copiés. Ainsi, s'il n'y a pas de caractère nul dans les n premiers octets de src, la chaîne résultante ne disposera de caractère nul final. Dans le cas où la longueur src est inférieure à n, la fin de dest sera remplie avec des caractères nuls.
@Gurney : Dans ta solution pense bien à rajouter le '\0' final après avoir utilisé strncpy. Car elle ne le rajoute pas automatiquement comme le dit le manuel.
Non, il n'y a aucun problème sauf que cela pourrait introduire des débutants en erreur (s'ils ont survolé la citation du manuel).
De ce fait, je l'aurais mis de manière explicite pour qu'il voit qu'il y a un petit piège.
/* Petite ébauche de "zSommeChiffres"
* Par: --------- Realmagma --------*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int zSommeChiffres(int tableauNombre[], int tailleTableau);
int zSommeChiffresChar(char chaine[]);
int main()
{
int resultatSomme = 0, resultatChaine = 0;
int tableauNombre[6] = {1, 2, 3, 9, 5, 4}; /* Un petit tableau =D*/
char chaine[] = "1234";
resultatSomme = zSommeChiffres(tableauNombre, 6);
printf("La somme de mon tableau de nombre vaut %d\n\n", resultatSomme);
resultatChaine = zSommeChiffresChar(chaine);
printf("La somme de la suite de nombre vaut %d\n\n", resultatChaine);
return 0;
}
/*-----------------------------------------------------------------*/
int zSommeChiffres(int tableauNombre[], int tailleTableau)
{
int somme = 0, i;
for (i = 0; i < tailleTableau; i++)
somme = somme + tableauNombre[i];
return somme;
}
/*-----------------------------------------------------------------*/
int zSommeChiffresChar(char chaine[])
{
int i = 0, somme = 0;
for (i = 0; i < strlen(chaine); i++)
{
printf("chaine vaut %d\n", chaine[i]);
somme = somme + chaine[i] %8; /* Un modulo règle ne problème des valeurs en ascii*/
}
return somme; /* On retourne la somme*/
}
J'ai quelque problèmes:
1-) Dans ma fonction "zSommeChiffresChar" je calcule la somme de mes chiffres en... ascii
Au lieu d'obtenir 10, j'ai 202:
#include <stdio.h>
#include <string.h>
int zSommeStr(const char *), zSommeInt(const long long);
int zMax(const long long, const int);
int main (int argc, const char * argv[]) {
printf("%d avec zSommeStr\n", zSommeStr("800000"));
printf("%d avec zSommeInt\n", zSommeInt(800000));
printf("Nombre de nombres entre 1 et 30 dont "
"la somme des chiffres est 3 : %d\n", zMax(30, 3));
return 0;
}
int zSommeStr(const char * str) {
int len = strlen(str), res = 0, i;
for (i = 0; i < len; i++)
res += str[i]-'0'; /* On enlève '0' pour revenir à la *
* valeur du nombre depuis l'ASCII. */
return res;
}
int zSommeInt(const long long nb) {
int res = 0;
long long i;
for (i = 1; i < nb; i*=10)
res += (nb%(i*10)-nb%(i))/i;
/* On sépare d'abord le chiffre qu'on veut *
* (suivi d'un ou plusieurs zéros), et on *
* enlève les zéros. */
return res;
}
int zMax(long long nb, int val) {
long long i, res = 0;
for (i = 0; i < nb+1; i++)
if (zSommeInt(i) == val)
res++;
return res;
}
Non, il n'y a aucun problème sauf que cela pourrait introduire des débutants en erreur (s'ils ont survolé la citation du manuel).
De ce fait, je l'aurais mis de manière explicite pour qu'il voit qu'il y a un petit piège.
Tu as raison, je corrige dans la journée...c'est corrigé.
Citation : realmagma
Dans ma fonction "zSommeChiffresChar" je calcule la somme de mes chiffres en... ascii
Au lieu d'obtenir 10, j'ai 202:
Merci GurneyH, pour la correction, elle est vraiment bien faite
Ahh, mais quelle différence entre les deux exercices
#include <stdio.h>
int zSommeChiffres(int n)
{
int s = 0;
for ( ; n ; n /= 10)
s += n % 10;
return s;
}
int zSommeChiffresChar(char *src)
{
int s = 0;
for ( ; *src ; src++)
s += *src - '0';
return s;
}
int main(void)
{
int n = 4863149;
char s[] = "4863149";
printf("%d -> %d\n", n, zSommeChiffres(n));
printf("%s -> %d\n", s, zSommeChiffresChar(s));
return 0;
}
Citation : GurneyH
Pour aller plus loin
Combien de nombres entre 0 et N ont la somme de leurs chiffres égale à S.
Par exemple entre 0 et 1000000000, combien de nombres ont la somme de leurs chiffres égale à 27 ?
Le problème pourra être résolu avec l'une ou l'autre des fonctions(ou avec les 2 ).
Ça c'est bien, je vais voir quand j'aurai du temps
realmagma > Il manque stdio.h (pour printf).
Dans ta fonction zSommeChiffresChar, tu as :
for (i = 0; i < strlen(chaine); i++)
A chaque tour de boucle, tu recalcules la taille de la chaîne ! Pourquoi ne pas faire une petite condition pour vérifier si l'on est arrivé à la fin de la chaîne ?
Ton modulo par contre ne donne pas le bon résultat : essaye avec la chaîne "123456789".
Une fois de plus, pour l'ASCII, tu veux passer de 48 à 0, de 49 à 1, etc...La solution est toute proche
Pouet_Forever > Mets ce que tu as mis en secret stp, il faut forcer les débutants à réfléchir.
realmagma > Il manque stdio.h (pour printf).
Dans ta fonction zSommeChiffresChar, tu as :
for (i = 0; i < strlen(chaine); i++)
A chaque tour de boucle, tu recalcules la taille de la chaîne ! Pourquoi ne pas faire une petite condition pour vérifier si l'on est arrivé à la fin de la chaîne ?
Ton modulo par contre ne donne pas le bon résultat : essaye avec la chaîne "123456789".
Une fois de plus, pour l'ASCII, tu veux passer de 48 à 0, de 49 à 1, etc...La solution est toute proche
Pouet_Forever > Mais ce que tu as mis en secret stp, il faut forcer les débutants à réfléchir.
Aussitôt dit, aussitôt fait .
int zSommeChiffresChar(char chaine[])
{
int i = 0, somme = 0;
for (i = 0; i < strlen(chaine); i++)
somme = somme + chaine[i] - '0'; /* -'0' pour ne pas aditionner leur valeur en ascii*/
if(chaine[i] == '\0')
return somme; /* On retourne la somme quand chaine[i] vaut '\0'*/
}
En revanche, je cherche encore pour la dernière partie.
Je vais y arriver vous verrez .
Pourquoi ne places-tu pas ta condition de fin de chaîne dans la boucle for ?
Ton code reboucle toujours autant là, et en plus, ta condition ne se situe même pas dans la boucle, donc elle est totalement inutile...
@ ttthebest : Ta fonction zSommeInt est inutilement compliquée ! Tu peux largement l'améliorer
@ realmagma :
Citation : realmagma
int zSommeChiffresChar(char chaine[])
{
int i = 0, somme = 0;
for (i = 0; i < strlen(chaine); i++)
somme = somme + chaine[i] - '0'; /* -'0' pour ne pas aditionner leur valeur en ascii*/
if(chaine[i] == '\0')
return somme; /* On retourne la somme quand chaine[i] vaut '\0'*/
}
En revanche, je cherche encore pour la dernière partie.
Je vais y arriver vous verrez .
Crée une variable pour strlen(chaine) ça t'évitera de le calculer à chaque tour de boucle
Ta dernière ligne est inutile ! Tu fais ta boucle tant qu'on est inférieur à strlen(chaine) donc après la sortie du for tu sera obligatoirement sur le '\0'
D'ailleurs si je ne m'abuse, ton compilateur devrait te donner un warning parce que tu n'as pas de retour de fonction 'normale'. Le retour ne se fait que dans une condition
D'ailleurs si je ne m'abuse, ton compilateur devrait te donner un warning parce que tu n'as pas de retour de fonction 'normale'
Non, il ne me dis rien.
J'ai corrigé pour le strlen, mais pas pour le if. En effet, je ne vois pas comment mettre ma condition dans le for:
for (i = 0; /* Condition */ ; i++)
Je vous redonne mon code (au cas où):
int zSommeChiffresChar(char chaine[])
{
int i = 0, somme = 0, tailleChaine = 0;
tailleChaine = strlen(chaine);
for (i = 0; i < tailleChaine; i++)
somme = somme + chaine[i] - '0'; /* -'0' pour ne pas aditionner leur valeur en ascii*/
return somme;
#include <stdio.h>
int zSommeChiffres(int n);
int zSommeChiffresEgale(int n,int somme);
int zSommeChiffresChar(const char* str);
int main(void)
{
int n1 = 92538;
int n2 = 1000000000;
int somme=27;
const char *chaine ="1459784612340012340123478942651012315647894561230123456789";
printf("%d -> %d\n", n1 , zSommeChiffres(n1));
printf("%s -> %d\n", chaine ,zSommeChiffresChar(chaine));
printf("entre 0 et %d , il y a %d nombre dont la somme est %d\n", n2 ,zSommeChiffresEgale(n2,somme),somme);
return 0;
}
int zSommeChiffres(int n)
{
int somme=n%10;
if (n==0)
return 0;//somme;
return somme+zSommeChiffres(n/10);
}
int zSommeChiffresChar(const char* str)
{
if (*(str)=='\0')
{
return 0;
}
int somme=(*(str)-48);
return somme+zSommeChiffresChar(str+1);
}
int zSommeChiffresEgale(int n,int somme)
{
int i;
int nombreSommeEgale=0;
for (i=0;i<=n;i++)
{
if (zSommeChiffres(i)==somme)
{
nombreSommeEgale++;
}
}
return nombreSommeEgale;
}
et les résulats
92538 -> 27
1459784612340012340123478942651012315647894561230123456789 -> 228
entre 0 et 1000000000 , il y a 14033305 nombre dont la somme est 27
Process returned 0 (0x0) execution time : 126.703 s
Press any key to continue.