OVH Cloud OVH Cloud

Meilleure façon de demander la date ?

39 réponses
Avatar
Eddahbi Karim
Bonjour, Bonsoir,

Voilà je continue les exercices de mon bouquin de C.
J'en suis au chapitre concernant les structures et ils demandent de créer
une structure contenant le temps et la date. (Puis de calculer l'espace
entre deux temps en minutes).

Ma question est :

Est-il mieux de demander à l'utilisateur de rentrer la date comme ceci :
- mm/jj/aaaa
Puis de prendre les valeurs mm et jj, avec un srtoi (C99) ou un strtod
(Ansi) quit à caster en int après, et de les vérifier. Ou
- mm jj aaaa
Puis de prendre après les valeurs à l'aide d'un sscanf(input,"%d %d %d",
...) pour les vérifier après.

Et pour le temps vous avez une idée ?
J'ai pensé que la méthode 88:88 serait pas mal mais si vous avez une autre
idée ;).

Merci,
Voila ;),
ThE_TemPLaR

10 réponses

1 2 3 4
Avatar
AG
c'est pas pour faire mon enculeur de mouches, mais des fois il y a
"if(send)", d'autres fois "if(send!=NULL)",... Enfin moi je dis ça, je
dis rien...
Avatar
EpSyLOn
On Fri, 18 Jul 2003 09:54:33 +0200, Eddahbi Karim
wrote:


In 'fr.comp.lang.c', Eddahbi Karim
wrote:




[snip long message]

Une des regles d'Usenet est de ne quoter que ce qui est nécéssaire à
la compréhension de la branche du thread. Le reste on snippe. Alors
quand tu quotes 1012 lignes pour en rajouter 13 (et encore je suis
sympa, je te compte les lignes du début), qui en plus, ne rajoutent
rien a la conversation, tu encombres le réseau pour rien (certains
payent Internet au temps, merci de penser a ceux la).

www.usenet-fr.net (le lexique)
http://www.giromini.org/usenet-fr/repondre.html
Ces deux documents te seront utiles.

--
___
| | |/
sas.epsylon<at>wanadoo.fr | | (o o)
______ ______ _____ __ _ | |oOO_(_)_OOo
/ __ Y / ____/ / !' |/
| ____/ |__> |____ ` | |_ O | ^ |
_____/| ___/______X_ Y_______|___/__||__|
~*~ |__| ____/


Avatar
Tobias Oed
Eddahbi Karim wrote:

Bonjour, Bonsoir,

Voilà je continue les exercices de mon bouquin de C.
J'en suis au chapitre concernant les structures et ils demandent de créer
une structure contenant le temps et la date. (Puis de calculer l'espace
entre deux temps en minutes).

Ma question est :

Est-il mieux de demander à l'utilisateur de rentrer la date comme ceci :
- mm/jj/aaaa
Puis de prendre les valeurs mm et jj, avec un srtoi (C99) ou un strtod
(Ansi) quit à caster en int après, et de les vérifier. Ou


Qu'est ce qui cloche avec strtol?
Tobias.

--
unix http://www.faqs.org/faqs/by-newsgroup/comp/comp.unix.programmer.html
clc http://www.eskimo.com/~scs/C-faq/top.html
fclc (french): http://www.isty-info.uvsq.fr/~rumeau/fclc/

Avatar
Emmanuel Delahaye
In 'fr.comp.lang.c', AG wrote:

c'est pas pour faire mon enculeur de mouches, mais des fois il y a
"if(send)", d'autres fois "if(send!=NULL)",... Enfin moi je dis ça, je
dis rien...


"Faites ce que je dis, mais ne dites pas ce que je fais!"

Comme je l'ai précisé, je n'ai pas triché. C'est du code industriel, du vrai
avec une histoire et tout. Il est améliorable, tant mieux!

--
-ed- [remove YOURBRA before answering me]
The C-language FAQ: http://www.eskimo.com/~scs/C-faq/top.html
<blank line>
FAQ de f.c.l.c : http://www.isty-info.uvsq.fr/~rumeau/fclc/

Avatar
Eddahbi Karim

Qu'est ce qui cloche avec strtol?
Tobias.


C'est long et fastidieux pour gérer toutes les erreurs :).

sscanf(input,"%02d/%02d/%04d", &month, &day, &year);

Avec des vérifications sur l'input avant, c'est moins long :)

Voila ;),
ThE_TemPLaR

Avatar
Marc Lasson
Claudio a écrit:

"diviser pour régner", telle devrait etre la devise de tout bon
programmeur !


Jusqu'a ce que les anglais comprirent que "l'union fait la force".

--
Marc, n'est pas un bon programmeur.

Avatar
Eddahbi Karim
Voilà,

J'ai déjà fait mon bout de code pour la date,
J'ai expérimenté les structures, passages de structure, retour de
structures et tout autre joyeuseté :).
(d'ailleurs les postes de Delahaye et de Serge Paccalin m'ont bien aidé
:))

Ça donne ça :

(Le main est un main vite fait, le vrai main sera dans le programme final)

#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

/************************Structure************************************

NAME : date

CONTAINS :
- The input to insert the date
- The month, day and year taken from the input
- The error value to solve problem cases

*********************************************************************/

typedef
struct {
char input[12];
unsigned short int month;
unsigned short int day;
unsigned short int year;
unsigned short int err;
} date;

/*********************************************************************

NAME : wipestring( string )

USAGE : Wipe useless values from the string...

PARAMETERS : char const *string to wipe...

*********************************************************************/

static void
wipestring (char const *string)
{
/* We search a 'n' character */
char *p = strchr (string, 'n');

/* We found it, we replace it by '' */
if (p)
{
*p = '';
}
/* We don't find it, so we get everything pending in stdin */
else
{
int c;

while ((c = getchar ()) != 'n' && c != EOF)
{
}
}
}

/*********************************************************************

NAME : getstring ( string, size )

USAGE : Take the user input and verify it...

PARAMETERS :
- The string to check and to fill if the user enter something
- The size of the string

RETURNS :
- EXIT_SUCCESS
- EXIT_FAILURE

*********************************************************************/

static int
getstring (char *string, size_t size)
{
/* The error number */
int err = EXIT_SUCCESS;

/* Check if there's an input */
if (fgets (string, (int) size, stdin) == NULL)
{
printf ("Input Errorn");
err = EXIT_FAILURE;
}

/* Check if the user entered something */
else
{

if ( strchr(string, 'n') == NULL )
{
printf("Input too large.n");
wipestring(string);
err = EXIT_FAILURE;
}

/* If we got a 'n', does he enter something ? */
if ( err == EXIT_SUCCESS )
{

/* Wipe useless values */
wipestring (string);

if (*string == '')
{
printf ("No inputn");
err = EXIT_FAILURE;
}

}
}

return err;
}

/***********************************************************************

NAME : checkdate ( month, day, year )

PARAMETERS :
- unsigned short int day which will be check with the month and the
year
- unsigned short int month
- unsigned short int year

RETURNS :
- EXIT_SUCCESS if no problem.
- EXIT_FAILURE if there was a wrong value.

***********************************************************************/

static int
checkdate ( unsigned short int month, unsigned short int day,
unsigned short int year)
{
/* The number of days in each month.
There's 13 values, because the first value is 0 and month 0
doesn't exist */
unsigned short int days_in_month[13] = {
0,
31,
28,
31,
30,
31,
30,
31,
31,
30,
31,
30,
31
};

/* The error value */
int err = EXIT_SUCCESS;

/* If the year is leap, the maximum isn't 28 but 29 */
if ( (year % 4 == 0) &&
(( year % 100 != 100 ) || ( year % 400 == 0 ))
)
{
days_in_month[2] = days_in_month[2] + 1;
}

/* If the day or the month isn't valid at all, we don't continue */
if ((day < 1 ) || (day > 31) || (month < 1) || (month > 12))
{
fprintf(stderr, "Month or day invalid.n");
err = EXIT_FAILURE;
}
/* If the day is superior to the max in the month entered */
else if ( day > days_in_month[month] )
{
(void) fprintf(stderr, "Day invalid according to the month.n");
err = EXIT_FAILURE;
}

return err;

}



/***********************************************************************

NAME : takedate( *p_usertime )

PARAMETERS :
- Date structure

RETURN :
- Date structure

***********************************************************************/


static date
takedate (date *p_usertime)
{
/* Improve visibility with clear names */
unsigned short int month = p_usertime->month;
unsigned short int day = p_usertime->day;
unsigned short int year = p_usertime->year;
unsigned short int err = p_usertime->err;

/* Debug case : The variables MUST be initialized */
assert ( month == 0 && day == 0 && year == 0 );

(void) printf("Enter a date (mm/dd/yyyy) : ");
(void) fflush(stdout);

/* If nothing is wrong with the input itself, we check if the user
followed the instructions */
err = getstring( p_usertime->input, sizeof(p_usertime->input) );

if ( err == EXIT_SUCCESS )
{
if ( sscanf(p_usertime->input, "%02hu/%02hu/%04hu",
&month, &day, &year) != 3 )
{
(void) fprintf(stderr, "Invalid date or bad separator.n");
err = EXIT_FAILURE;
}
}

/* If nothing is wrong, check if the date is correct */
if ( err == EXIT_SUCCESS )
err = checkdate(month, day, year);

if ( err == EXIT_SUCCESS )
printf("You have entered : %s.n", p_usertime->input);

/* This is the drawback of the visibility */
p_usertime->month = month;
p_usertime->year = year;
p_usertime->day = day;
p_usertime->err = err;

return *p_usertime;
}

int
main (void)
{

/* The initialisation of the array of structures
input, month, day, year, err */
date userdate[2] = {
{{0}, 0, 0, 0, EXIT_SUCCESS},
{{0}, 0, 0, 0, EXIT_SUCCESS}
};

/* Test */
userdate[0] = takedate (&userdate[0]);

/* If we got an error, we don't continue */
if ( userdate[0].err == EXIT_SUCCESS )
{
takedate(&userdate[1]);
}
/* and we set the error to EXIT_FAILURE */
else { userdate[1].err = EXIT_FAILURE; }

return userdate[1].err;
}

Voilà, si vous avez tout type de commentaire à me faire :).
Merci ;),
Voila,
ThE_TemPLaR
Avatar
Eddahbi Karim

Eddahbi Karim wrote:


[snip]


C'est vachement tordu d'avoir ton erreur incluse dans la structure. Si
tu penses en termes objets (un petit peu, hein, je veux pas de OOP
masters qui me tombent sur le coin du rable pour me reprendre !), ce qui
correspond a mettre tes elements dans une meme structure, les membres
d'une structure sont les attributs de l'objet represente par cette
structure.


J'ai moyennement compris :).
C'est pour avoir une gestion d'erreur, or comme je ne peux renvoyer qu'une
structure, j'ai mis la gestion d'erreur dans la structure, maintenant, y'a
peut être un autre moyen, mais je ne vois pas lequel.


Une date, telle que tu la concois, c'est une annee, un mois et un jour,
ainsi que (pourquoi pas, mais ca peut se discuter) une chaine de
caracteres. Ca se discute dans la mesure ou tu peux recreer la chaine de
caracteres a la volee avec la fonction (methode) adequate. Ce n'est pas
forcement un probleme d'avoir de la redondance dans les infos portees
par tes membres, mais il faut etre sur que tu n'aies pas de problemes
d'inconsistance.


C'est vrai que l'input ne me sert pas trop, je vais voir si je peux pas
l'enlever de la structure et juste le recréer temporairement dans la
fonction adéquate :).


Bref, revenons a nos moutons. Une erreur, ce n'est pas vraiment un
attribut de ta date. Donc l'erreur, elle n'a vraiment rien a faire parmi
les membres de ta structure. AMHA, bien entendu.


Vi, mais il me faut savoir si tout c'est bien passé après la fonction.
Je me vois pas mal retourner return EXIT_FAILURE et rattrapé le tout avec
une structure (d'ailleurs mon compilateur va crier et refuser de compiler
:))


static void
wipestring (char const *string)
{
/* We search a 'n' character */
char *p = strchr (string, 'n');

/* We found it, we replace it by '' */ if (p)
{
*p = '';
}
/* We don't find it, so we get everything pending in stdin */ else
{
int c;

while ((c = getchar ()) != 'n' && c != EOF) {
}
}
}

wipestring, admettons. C'est une maniere de faire. L'autre maniere de

faire, c'est de s'arranger pour recuperer une ligne entiere a chaque
fois (a grands coups de fgets et de realloc). Une bonne (AMHA)
implementation est ggets.


Bah c'était ce que m'avais donné Delayahe, et c'était en coordination
avec la FAQ.


static int
getstring (char *string, size_t size) {



[snip]

/* If we got a 'n', does he enter something ? */ if ( err = >> EXIT_SUCCESS )
{

/* Wipe useless values */
wipestring (string);
La, je vois pas bien l'interet de l'appel a wipestring. Une fois que tu

as recupere la ligne qui t'interesse (recupere en entier !), le reste
des donnees se trouvant sur stdin appartient a la suite de ton
programme. Ainsi, en imaginant que tu aies, quelque part en chemin un
appel bloquant, qui laisse quelques secondes a ton utilisateur. Il sait
que tu vas demander une autre date. Il n'a pas envie d'attendre le
prompt pour la rentrer. Au contraire, il la rentre pendant que toi tu
fais le reste de ton traitement, avant meme d'arriver a la ligne
ci-dessus. Et toi, boum ! Tu lui vires tout ce qu'il a entre. D'un point
de vue interface utilisateur, c'est pas genial :-/


D'un autre coté, bash exécute ce qui reste dans stdin (je ne sais pas
pourquoi, mais c'est comme ça). Je ne peux pas me permettre un buffer
overflow AVEC un exécution de code arbitraire parce que l'utilisateur n'a
pas voulu attendre 3 secondes :-).

[snip]

}


static int
checkdate ( unsigned short int month, unsigned short int day,
unsigned short int year)



[snip]

C'est un peu dommage. A cause de ce test, tu ne peux pas declarer ton
tableau de jours-par-mois comme static. Je pense que le test de l'annee
bissextile doit se faire ci-dessous au moment ou tu tests si le nombre
de jours dans le mois est correct.


J'avais pas raisonné comme ça, c'est vrai que c'est pas mal :)


D'un point de vue clarte de l'algorithme, je ne pense pas non plus que
ce soit tres bien de tester d'abord si les jours sont compris entre 1 et
31 et ensuite si les jours sont compris entre 1 et le nombre max de
jours du mois.


Ça m'apprendra à faire bouger mes fonctions. Au départ je voulais la
mettre dans la fonction takedate, au moins takedate n'aurait pas eu de
jour au dessus de 31 à vérifier, puis j'ai voulu mettre tout ce qui
était vérification dans une seule fonction... Ça crée de la redondance
assez inutile :/


/* If the day or the month isn't valid at all, we don't continue */
if ((day < 1 ) || (day > 31) || (month < 1) || (month > 12)) {
fprintf(stderr, "Month or day invalid.n"); err = EXIT_FAILURE;
}
/* If the day is superior to the max in the month entered */ else
if ( day > days_in_month[month] ) {
(void) fprintf(stderr, "Day invalid according to the
month.n"); err = EXIT_FAILURE;
}


Allez, on remplace tout ca:



[snip]

En deux fois... pourquoi pas :)

/* traitement special pour fevrier */ else if (
(2 == month) &&
(year % 4 == 0) &&
(( year % 100 != 0 ) || ( year % 400 == 0 )) && ((day < 1) || (day
days_in_month[month] + 1)))
{

fprintf(stderr, "Invalid day.n");
err = EXIT_FAILURE;
}


Ben day > 29 serait pas mieux :), c'est plus clair non ?

[snip]


}

static date
takedate (date *p_usertime)
{



[snip]


/* Debug case : The variables MUST be initialized */
Pourquoi ?



Parce que je ne veux pas de variable non initialisé avant utilisation.
Ça me permet de ne pas avoir de UB si je modifie mon programme un jour,
en mettant par exemple month = month + 1 (C'est à titre d'exemple :)).
Bref je suis maniaque :)

[snip]

/* This is the drawback of the visibility */
Oui, en l'occurence, une autre maniere d'ameliorer la "visibilite" est

d'avoir un nom de variable un peu plus raisonnable, e.g. date ou pdate.


Oui d'ailleurs faut que j'arrête de coder les yeux à moitié ouvert,
c'est userdate et pas usertime :)

p_usertime->month = month;
p_usertime->year = year;
p_usertime->day = day;
p_usertime->err = err;

return *p_usertime;
Pourquoi ? A quoi ca rime de retourner la structure elle meme ?!?! 1) Ca

enleve l'interet de passer un pointeur. Tant qu'a passer des structures
entieres, autant le faire a l'aller et au retour. 2) Ca n'apporte...
rien !
3) Ca t'empeche de retourner un code d'erreur a la place de la
structure, ce qui t'oblige a inclure le code d'erreur parmis les membres
de la structure, ce qui est une faute de conception, comme j'ai essaye
de l'exppliquer ci-dessus.

Donc non, non, non. Definissons:
static int takedate(date *date)
{
/* on veut pouvoir remplir la structure, donc elle
doit deja pointer quelque part */
assert(NULL != date);

/* etc. */

return r;
}


J'avais pas testé ça effectivement :/ Je suis mis dans la tête que les
modifications étaient temporaires...


}

int
main (void)
{
int err;



[snip]

/* Test */
err = takedate(userdate);

if (EXIT_SUCCESS == err)
{
err = takedate(userdate + 1);
}
return err;
}

Merci ;),


De rien.


C'est vrai que c'est plus clair, merci je vais arranger ça :)

Voila,
ThE_TemPLaR


Avatar
Eddahbi Karim


D'un autre coté, bash exécute ce qui reste dans stdin (je ne sais pas
pourquoi, mais c'est comme ça). Je ne peux pas me permettre un buffer
overflow AVEC un exécution de code arbitraire parce que l'utilisateur n'a
pas voulu attendre 3 secondes :-).


Woops, parler trop vite, c'est juste une exécution de code, pas de Buffer
Overflow

Voila ;),
ThE_TemPLaR

Avatar
AG
C'est pour avoir une gestion d'erreur, or comme je ne peux renvoyer qu'une
structure, j'ai mis la gestion d'erreur dans la structure, maintenant, y'a
peut être un autre moyen, mais je ne vois pas lequel.


Souvent, les fonctions ont un pointeur en argument qui permet de remplir
une structure : ce sont les valeurs de retour de la fonction.
Par ailleurs, la fonction renvoie un int dont la valeur indique le
déroulement de l'opération :

exemple :

typedef struct complex_s
{
double a;
double b;
} complex_t;

int init_complex(complex_t * z)
{
if(z)
{
z->a=0;
z->b=0;
return 0;
}
else
{
fprintf(stderr,"invalid pointern");
return 1;
}
}

c'est comme cela que fonctionnent beaucoup de fonctions de la
biliothèque standard.

Alexandre.

1 2 3 4