Lunghezza massima della stringa usando scanf -> ANSI C

Io ho:

#define MAX_STR_LEN 100 

e voglio mettere in modello scanf in modo da poter controllare la lunghezza della stringa:

 scanf("%100[^\n]s",sometext) 

Provai:

 scanf("%MAX_STR_LEN[^\n]s",sometext) scanf("%"MAX_STR_LEN"[^\n]s",sometext) scanf("%",MAX_STR_LEN,"[^\n]s",sometext) 

E non ha funzionato. Voglio solo evitare l’overflow del buffer poiché “sometext” è allocato con malloc(MAX_STR_LEN)

Qualche idea?

Non ero soddisfatto di nessuna di queste soluzioni, quindi ho fatto ulteriori ricerche e scoperto la macro- configurazione GNU GCC

che può essere usato come:

 #define XSTR(A) STR(A) #define STR(A) #A #define MAX_STR_LEN 100 scanf("%"XSTR(MAX_STR_LEN)"[^\n]s", sometext) 

Forse VS2010 offre qualcosa di simile?

Voglio solo evitare l’overflow del buffer

Quindi non utilizzare scanf() . Affatto.

Se stai scannerizzando righe di testo, non #define MAX_STR neanche. Puoi LINE_MAX in (se stai mirando ai sistemi POSIX compatibili):

 char buf[LINE_MAX]; fgets(buf, sizeof(buf), stdin); 

dovrebbe fare il trucco

Come quasi tutti dicono, è meglio usare fgets(..., stdin) per gestire questo problema.

Nel seguente link, ho suggerito una tecnica sicura e corretta che consente di sostituire scanf() con un metodo più sicuro, mediante una macro solida:

Una macro che sostituisce in modo sicuro scanf ()

La macro che ho proposto (lavorando con compilatori C99 compatibili) è safe_scanf() , come mostrato nel seguente programma:

 #include  #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\tf == %g", x, f); return 0; } 

Dovresti sintonizzare il valore di MAXBUFF base alle tue esigenze …
Sebbene la macro safe_scanf() sia piuttosto solida,
ci sono alcuni punti deboli nell’usare l’approccio macro:
Mancano controlli di tipo per i parametri, mancano i valori di “ritorno” (che difficilmente differisce dalla funzione “true” scanf() , che restituisce un int , con informazioni preziose per il controllo degli errori), e così via.
Tutti i problemi hanno una soluzione, ma fa parte di un altro argomento …

Forse, la soluzione più esatta è definire una funzione my_scanf() con numero variabile di parametri, richiamando la libreria stdarg.h , unita a una combinazione di fgets() e vsscanf() . Qui hai il codice:

 #include  #include  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:\nx == %d\nz == %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, ...); 

Accetta una stringa di formato fmt che si comporta allo stesso modo di qualsiasi altro scanf() -like.
Il 2 ° parametro è il numero massimo di caratteri che saranno effettivamente accettati dallo standard input (tastiera).
Il valore di ritorno è un int , che è EOF se maxbuff non ha senso, o bene è successo qualche 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 aggiuntivo '\ 0'.
I valori non positivi di maxbuff vengono immediatamente scartati.
fgets() leggerà una stringa viene letta da stdin (tastiera) con spazio per la maggior maxbuff caratteri maxbuff , incluso '\ n'.
Se l'utente ha inserito una stringa molto lunga, verrà troncata e sarà necessario un qualche tipo di meccanismo "flush" per scartare tutti i caratteri al successivo '\ n' ( INVIO ). 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 lo stdin .
Questo è il caso se, e solo se, buffer[maxbuff - 1] non è uguale a '\ 0' né '\ n'.
( Controllalo! )
Infine, una combinazione stdarg.h macro stdarg.h e la funzione vsscanf() viene utilizzata per elaborare l'elenco di variabili delle variabili.

Consiglia l’approccio fgets(buffer, sizeof(buffer), stdin) .

Se si desidera utilizzare ancora scanf (), è ansible crearne il formato in fase di esecuzione.

 #define MAX_STR_LEN 100 char format[2 + sizeof(size_t)*3 + 4 + 1]; // Ugly magic # sprintf(format, " %%%zu[^\n]", (size_t) MAX_STR_LEN); scanf(format, sometext); 

o ridefinisci MAX_STR_LEN come stringa

 #define MAX_STR_LEN "100" scanf(" %" MAX_STR_LEN "[^\n]", sometext); 

Raccomando comunque i fgets() .
Nota fgets() inserirà gli spazi iniziali e il trailing \n nel buffer mentre " %[^\n]" non lo farà.
BTW: non è probabile che i trailing nei tuoi formati facciano ciò che pensi.

Che ne dite di

 scanf("%.*[^\n]s", MAX_STR_LEN, sometext)