2010-10-26 11 views
38

voglio leggere il nome inserito dal mio utente utilizzando programmi Ccome leggere stringa immessa dall'utente in c

Per questo ho scritto:

char name[20]; 

printf("Enter name:"); 
gets(name); 

ma utilizzando gets non è buono così mi suggeriscono un modo migliore.

risposta

65

Si dovrebbe mai uso gets (o scanf con una dimensione di stringa illimitata) dal momento che si apre fino a buffer overflow. Utilizzare lo fgets con un handle stdin poiché consente di limitare i dati che verranno inseriti nel buffer.

Ecco un piccolo frammento che uso per ingresso di linea da parte dell'utente:

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

#define OK  0 
#define NO_INPUT 1 
#define TOO_LONG 2 
static int getLine (char *prmpt, char *buff, size_t sz) { 
    int ch, extra; 

    // Get line with buffer overrun protection. 
    if (prmpt != NULL) { 
     printf ("%s", prmpt); 
     fflush (stdout); 
    } 
    if (fgets (buff, sz, stdin) == NULL) 
     return NO_INPUT; 

    // If it was too long, there'll be no newline. In that case, we flush 
    // to end of line so that excess doesn't affect the next call. 
    if (buff[strlen(buff)-1] != '\n') { 
     extra = 0; 
     while (((ch = getchar()) != '\n') && (ch != EOF)) 
      extra = 1; 
     return (extra == 1) ? TOO_LONG : OK; 
    } 

    // Otherwise remove newline and give string back to caller. 
    buff[strlen(buff)-1] = '\0'; 
    return OK; 
} 

Questo mi permette di impostare la dimensione massima, in grado di rilevare se viene inserito troppi dati sulla linea, e sarà svuotare il resto della linea e quindi non influisce sulla successiva operazione di input.

È possibile testare con qualcosa di simile:

// Test program for getLine(). 

int main (void) { 
    int rc; 
    char buff[10]; 

    rc = getLine ("Enter string> ", buff, sizeof(buff)); 
    if (rc == NO_INPUT) { 
     // Extra NL since my system doesn't output that on EOF. 
     printf ("\nNo input\n"); 
     return 1; 
    } 

    if (rc == TOO_LONG) { 
     printf ("Input too long [%s]\n", buff); 
     return 1; 
    } 

    printf ("OK [%s]\n", buff); 

    return 0; 
} 
+1

non le librerie di sistema che implementano scanf impediscono l'overflow del comando (ho capito che all'interno del programma se lo sviluppatore non ha verificato l'input ci potrebbe essere un overflow, ma la libreria di sistema è sicura giusto?). – Marm0t

+8

No, se si 'scanf ("% s ")' in un buffer da 20 byte e l'utente inserisce una riga da 40 byte, si è in stato di hosed. L'intero punto di 'scanf' è formattato per la scansione e c'è poco più _unformatted_ dell'input dell'utente :-) – paxdiablo

+5

@ Marm0t - Pensa in questo modo considerando la seguente domanda: in che modo l'implementazione previene un overflow se tutto ciò che ottiene è un puntatore a una porzione di memoria (tipizzata come un char *) 'senza alcun parametro che indichi l'implementazione sulla dimensione del buffer di destinazione'? –

5

Su un sistema POSIX, probabilmente dovreste usare getline se è disponibile.

È anche possibile utilizzare la funzione di dominio pubblico di Chuck Falconer ggets che fornisce la sintassi più vicina a gets ma senza i problemi. (Il sito web di Chuck Falconer non è più disponibile, anche se archive.org has a copy, e ho fatto my own page for ggets.)

+0

Con l'avvertenza che non funziona come previsto con i file che terminano solo in un CR. Quelli non sono così rari come si potrebbe immaginare (dice, dopo aver incontrato una cartella piena di loro). È un'occasione mancata che non hanno permesso a getdelim/getline di prendere una lista di delimitatori invece di un singolo int. –

+0

@MauryMarkowitz Funzionerebbe su un sistema che utilizza CR come formato di fine linea nativo. I flussi in modalità testo convertiranno qualsiasi carattere nativo in "\ n'. – jamesdlin

15

Penso che il modo migliore e più sicuro per leggere le stringhe inserite dall'utente sta usando getline()

Ecco un esempio di come fare questo:

#include <stdio.h> 
#include <stdlib.h> 
int main(int argc, char *argv[]) 
{ 
    char *buffer = NULL; 
    int read; 
    unsigned int len; 
    read = getline(&buffer, &len, stdin); 
    if (-1 != read) 
     puts(buffer); 
    else 
     printf("No line read...\n"); 

    printf("Size read: %d\n Len: %d\n", read, len); 
    free(buffer); 
    return 0; 
} 
+1

read = getline (& buffer, & len, stdin); fornisce avvisi GCC ad esempio: gcc -Wall -c "getlineEx2.c" getlineEx2.c: nella funzione principale: getlineEx2.c: 32: 5: avviso: passaggio dell'argomento 2 di getline dal tipo di puntatore incompatibile [abilitato per impostazione predefinita] read = getline (& buffer, & len, stdin); ^ Nel file incluso da /usr/include/stdio.h:29:0, da getlineEx2.c: 24: /usr/include/sys/stdio.h:37:9: nota: previsto size_t * ma argomento è di tipo unsigned int * ssize_t _EXFUN (getline, (char **, size_t *, FILE *)); ^ La compilazione è terminata correttamente. – rpd

+1

L'aggiornamento di getline ora richiede che len sia size_t o unsigned long – Wes

+0

lato negativo: POSIX ma non ANSI C. –

1

È possibile utilizzare la funzione scanf per leggere stringa

scanf("%[^\n]",name); 

non so su altre opzioni migliori per ricevere stringa,

2

Ho trovato una soluzione facile e piacevole:

char*string_acquire(char*s,int size,FILE*stream){ 
    int i; 
    fgets(s,size,stream); 
    i=strlen(s)-1; 
    if(s[i]!='\n') while(getchar()!='\n'); 
    if(s[i]=='\n') s[i]='\0'; 
    return s; 
} 

si basa su fgets ma libero da '\ n' e stdin personaggi extra (per sostituire fflush (stdin) che non funziona su tutto il sistema operativo, utile se devi acquisire stringhe dopo questo).

0

Sui sistemi BSD e Android è anche possibile utilizzare fgetln:

#include <stdio.h> 

char * 
fgetln(FILE *stream, size_t *len); 

Come così:

size_t line_len; 
const char *line = fgetln(stdin, &line_len); 

Il line non è NULL terminato e contiene \n (o qualunque sia la vostra piattaforma sta usando) in fine. Diventa non valido dopo la successiva operazione di I/O sul flusso. È consentito modificare il buffer restituito line.

Problemi correlati