Come quasi tutti dicono, è meglio utilizzare fgets(..., stdin)
per gestire questo problema.
Nel seguente link, mi hanno suggerito una tecnica sicura e corretta che consentono di sostituire scanf()
con un metodo più sicuro, per mezzo di un solido macro:
A macro that safely replaces scanf()
La macro che ho proposto (lavorando con compatibili C99 compilatori) è safe_scanf()
, come mostrato nella seguente programma:
#include <stdio.h>
#define safe_scanf(fmt, maxb, ...) { \
char buffer[maxb+1] = { [maxb - 1] = '\0' }; \
fgets(buffer, maxb+1, stdin); \
if ((buffer[maxb - 1] != '\0') && (buffer[maxb - 1] != '\n')) \
while(getchar() != '\n') \
; \
sscanf(buffer, fmt, __VA_ARGS__); \
}
#define MAXBUFF 20
int main(void) {
int x; float f;
safe_scanf("%d %g", MAXBUFF+1, &x, &f);
printf("Your input was: x == %d\t\t f == %g", x, f);
return 0;
}
si dovrà sintonizzare il valore di MAXBUFF
base alle vostre esigenze ...
Anche se la macro safe_scanf()
è piuttosto solido,
ci sono alcuni debolezza utilizzando l'approccio macro:
mancante tipo-controllo per i parametri mancano i valori di "ritorno" (che difficilmente differiscono dalla funzione "vera" scanf()
, che restituisce un int, con informazioni preziose per il controllo degli errori), e così via.
Tutto ciò che i problemi hanno una soluzione, ma è parte di un altro argomento ...
Forse, la soluzione più precisa è quella di definire una funzione my_scanf()
con numero variabile di parametri, invocando la biblioteca stdarg.h
, congiunta ad una combinazione di fgets()
e vsscanf()
. Qui si ha il codice:
#include <stdio.h>
#include <stdarg.h>
int my_scanf(const char* fmt, const unsigned int maxbuff, ...) {
va_list ptr;
int ret;
if (maxbuff <= 0)
return EOF; /* Bad size for buffer[] */
char buffer[maxbuff+1];
buffer[maxbuff-1] = '\0'; /* Quick buffer cleaning... */
if (fgets(buffer, maxbuff+1, stdin) == NULL)
return EOF; /* Error detected */
else {
if ((buffer[maxbuff-1] != '\n') && (buffer[maxbuff-1] != '\0'))
/* Condition logically equivalent to:
fgets() has not reached an '\n'
*/
while (getchar() != '\n')
; /* "Flushing" stdin... */
va_start(ptr, maxbuff);
ret = vsscanf(buffer, fmt, ptr);
va_end(ptr);
return ret;
}
}
#define MAXBUFF 20
int main(void) {
int x;
float z;
int scanf_ret = my_scanf("%d %g", MAXBUFF, &x, &z);
printf("\nTest:\n x == %d\n z == %g\n scanfret == %d", x, z, scanf_ret);
getchar();
return 0;
}
La funzione my_scanf() ha il prototipo
int my_scanf(const char* fmt, const int maxbuff, ...);
Si accetta una stringa di formato fmt
che si comporta nello stesso modo di qualsiasi altra scanf()
-come fa.
Il 2 ° parametro è il numero massimo di caratteri che verrà effettivamente accettato dallo standard input (tastiera).
Il valore restituito è int int, che è EOF
se maxbuff
non ha senso, o bene si è verificato un errore di input. Se viene restituito un valore non negativo, è lo stesso che verrebbe restituito dalle funzioni standard sscanf()
o vsscanf()
.
All'interno della funzione, maxbuff
viene incrementato in 1, perché fgets()
crea spazio per un carattere '\ 0' aggiuntivo.
I valori non positivi di maxbuff
vengono immediatamente eliminati.
fgets()
leggerà una stringa viene letta da stdin
(tastiera) con spazio per al massimo maxbuff
caratteri, incluso '\ n'.
Se l'utente ha immesso una stringa molto lunga, verrà troncato e sarà necessario un meccanismo di "svuotamento" per scartare tutti i caratteri al successivo '\ n' (ENTER). In caso contrario, la prossima lettura della tastiera potrebbe avere caratteri precedenti, non desiderati affatto.
La condizione per "flushing" è che fgets()
non ha raggiunto '\ n' dopo aver letto stdin
.
Questo è il caso se, e solo se, buffer[maxbuff - 1]
non è uguale a "\ 0" né a "\ n".
(controllare!)
Infine, una combinazione appropriata di stdarg.h
macro e la funzione vsscanf()
è impiegato per elaborare la lista variabile di parametri.
Discusso [qui] (http://stackoverflow.com/questions/9457325/how-to-use-sscanf-correctly-and-safely). –
Come si può usare 'MAX_STR_LEN' in una stringa di formato? – amulous
@amolto, poiché è un rigoroso ANSI C, in realtà non ho accesso alla variabile stringa reale. Devo fare: char * somestring e poi malloc ... – tomdavies