2011-10-20 14 views
19

Sembra che ci siano molti modi per ottenere l'input dell'utente in C.Qual è il modo più semplice per ottenere l'input dell'utente in C?

Qual è il modo più semplice che richiede un piccolo codice?

Fondamentalmente ho bisogno di visualizzare questo:

Enter a file name: apple.text 

Fondamentalmente ho bisogno di chiedere all'utente per un nome di file. Quindi ho bisogno di qualcosa che ottenga solo quella parola che l'utente inserirà.

+0

Eventuali duplicati http://stackoverflow.com/questions/314401/how-to-read-a-line-from-the-console-in-c –

risposta

22

Il più semplice "corretto" modo è probabilmente questa, presa dalla carta di Bjarne Stroustrup Learning Standard C++ As A New Language.

(Nota: ho cambiato il codice di Bjarne per verificare isspace() invece che solo alla fine della riga Inoltre, a causa di @ matejkramny commento, da usare al posto di while(1)while(true) ... e fino a quando ci stanno abbastanza eretica. di modificare il codice di Stroustrup, ho subbed in C89 commenti invece di C++ stile troppo :-P)

#include <stdio.h> 
#include <ctype.h> 
#include <stdlib.h> 

void quit() /* write error message and quit */ 
{ 
    fprintf(stderr, "memory exhausted\n"); 
    exit(1); 
} 

int main() 
{ 
    int max = 20; 
    char* name = (char*) malloc(max); /* allocate buffer */ 
    if (name == 0) quit(); 

    printf("Enter a file name: "); 

    while (1) { /* skip leading whitespace */ 
     int c = getchar(); 
     if (c == EOF) break; /* end of file */ 
     if (!isspace(c)) { 
      ungetc(c, stdin); 
      break; 
     } 
    } 

    int i = 0; 
    while (1) { 
     int c = getchar(); 
     if (isspace(c) || c == EOF) { /* at end, add terminating zero */ 
      name[i] = 0; 
      break; 
     } 
     name[i] = c; 
     if (i == max - 1) { /* buffer full */ 
      max += max; 
      name = (char*) realloc(name, max); /* get a new and larger buffer */ 
      if (name == 0) quit(); 
     } 
     i++; 
    } 

    printf("The filename is %s\n", name); 
    free(filename); /* release memory */ 
    return 0; 
} 

che copre:.

  • saltare gli spazi fino a raggiungere carattere mettere
  • espandere il buffer di stringa in modo dinamico per soddisfare le stringhe di dimensioni arbitrarie
  • condizioni di manipolazione di quando la memoria non può essere allocato

Ci sono soluzioni più semplici, ma rotti, che potrebbe anche correre un po 'più veloce? Assolutamente!!

Se si utilizza scanf in un buffer senza limite per le dimensioni di lettura, l'input supera la dimensione del buffer, creerà un buco di sicurezza e/o un arresto anomalo.

Limitare la dimensione della lettura a, diciamo, solo 100 caratteri univoci di un nome di file potrebbero sembrare migliori di crash. Ma può essere peggio; ad esempio se l'utente intendeva (...)/dir/foo/bar.txt ma si finisce per erroneamente interpretare il proprio input e sovrascrivere un file chiamato bar.t che forse gli importava.

È meglio entrare presto in buone abitudini nell'affrontare questi problemi. La mia opinione è che se le vostre esigenze giustificano qualcosa di vicino al metallo e "C-like", vale la pena considerare il passaggio al C++. È stato progettato per gestire con precisione queste preoccupazioni - con tecniche robuste ed estendibili, ma con buone prestazioni.

+0

Anche se la tua classe vuole che tu scriva e vai avanti, ti suggerirei di mettere appunti ovunque tu faccia consapevolmente qualcosa di rotto/utile. Quindi accanto alle operazioni di lettura basta inserire un commento come// REVIEW: correttezza dell'input, vedere http://stackoverflow.com/questions/7831755/what-is-the-simplest-way-of-getting-user-input -in-c/*/'Mostra che fai la tua ricerca, mantiene il link per la lettura successiva. Inoltre chiunque esegua il debug di un codebase che sta ottenendo un crash misterioso può attirare la sua attenzione, vedendo non solo l'avviso ma anche un puntatore a una discussione sulla soluzione corretta. – HostileFork

+1

Btw la domanda chiede C non C++ –

+1

@matejkramny Nel documento cito e cito, Bjarne Stroustrup contrappone il "modo C" al "modo C++". Questo è il suo codice "C way" che sto presentando. Non è un codice C++. Il punto della carta è che questo codice C "corretto" può essere espresso in C++ in modo più saliente e senza perdita di efficienza; che potrebbe sorprendere i programmatori C durissimi. Ti assicuro che posso leggere la domanda; Ho solo pensato che una delle migliori risposte che avevo letto su come scrivere la versione C corretta di ciò che C++ fa per te provenga da un documento scritto che era un'opera di evangelizzazione del C++. Dovresti leggere il documento, anche se è vecchio. – HostileFork

1
#include <iostream> 
#include <string> 

using namespace std; 

int main() 
{ 
    cout << "Enter a file name:"; 

    string filepath; 
    cin >> filepath; 
} 
+2

Se erano * C++ *, certo .... quei piccoli * + 's * sono rilevanti ;-) –

+0

Beh, questo è in C++, che originariamente aveva la tua domanda taggata come. – Ayjay

+0

è quello C++? Non posso usare C++ – antonpug

3
#include <stdio.h> 
int main(void) 
{ 
    char file[100]; 
    printf("File Name?: \n"); 
    fgets(file, 100, stdin); 
    printf("Your input: %s", file); 
} 
+0

Malato! Mi sembra giusto! – antonpug

+1

Correlati: http://stackoverflow.com/questions/1252132/difference-between-scanf-and-fgets – HostileFork

7

scanf sembra funzionare in un ambiente non ostile. In altre parole, stai facendo un semplice programma di utilità per te stesso.

BUFSIZ di solito supera di gran lunga i limiti di dimensione dei percorsi UNIX.

#include <stdio.h> 

int main(void) { 
    char str[BUFSIZ];                                            

    printf("Enter a file name: ");                                         
    scanf("%s", str);                                            

    printf("You entered: %s\n", str);                                        
} 

Se avete bisogno di accumulare dati in un programma che potrebbe essere il bersaglio di sovraccarico del buffer, potrebbe essere necessario un po 'di più.

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

char * prompt_and_read(const char * prompt) { 
    char * response; 
    char * bufsize; 

    printf("%s", prompt); 
    asprintf(&bufsize, "%%%us", BUFSIZ); 

    if((response = malloc(BUFSIZ + 1)) == NULL) { 
    fprintf(stderr,"out of memory\n"); 
    exit(1); 
    } 
    scanf(bufsize, response); 
    free(bufsize); 
    return response; 
} 

int main (void) { 
    char * pathname; 

    pathname = prompt_and_read("Enter a file name: "); 
    printf("You entered: [%s]\n", pathname); 
    free(pathname); 

    return 0; 
} 
+1

Quindi cosa succede se la stringa supera BUFSIZ ...? – HostileFork

+0

Questo è veramente possibile, ma avevo l'impressione che volesse inserire nomi di file in un ambiente non ostile. ;-) Se fosse ostile, avrebbe dovuto fare qualcosa di un po 'più coinvolto. E ha chiesto una soluzione che includesse solo un piccolo codice. – karrick

+4

* Gli utenti * sono ostili ... :( –

1

si dovrebbe scrivere i propri fgets() la funzione wrapper che legge una riga di input in un buffer di dimensioni specifiche e che elimina il carattere di nuova riga che fgets() legge anche. si potrebbe anche restituire lo stato di se l'intera linea fosse o meno in grado di adattarsi al buffer (cioè è la nuova riga alla fine del buffer). non dovresti mai usare veramente scanf o gets a meno che tu non voglia overflow.

EDIT: pensato che potrei fornire qualche codice di base:

typedef unsigned char BOOL; 
#define TRUE 1 
#define FALSE 0 

/* wraps fgets, returns FALSE if the whole line couldn't fit into the buffer */ 
BOOL read_input(char *dest, size_t len) 
{ 
    /* our temp buffer needs to hold the extra new line char (len + 1) 
     * should also add error checking to malloc */ 
    BOOL bSuccess = TRUE; 
    size_t total_len = len + 1; 
    char *temp = (char*) malloc(total_len * sizeof(char)); 

    fgets(temp, total_len, stdin); 

    /* if the whole line wasn't read, we'll return FALSE but still copy temp into dest */ 
    if (temp[strlen(temp) - 1] != '\n') 
     bSuccess = FALSE; 
    else 
     temp[strlen(temp) - 1] = '\0'; /* overwrite the '\n' if it does infact exist */ 

    strcpy(dest, temp); 

    free(temp); 
    return bSuccess; 
} 
+0

Bene, questo è per un semplice (beh, non così semplice) incarico di programmazione. Voglio solo passare la lezione a questo punto. Non si complicherà troppo. Grazie comunque! – antonpug

+0

Quanto sopra può essere facilmente eseguito in meno di 10 righe, in realtà dipende da quante volte è necessario ripetere la funzionalità. A seconda dell'utilizzo del proprio input, anche il char di newline può talvolta causare problemi. E vale anche la pena notare che se fgets non è in grado di adattare l'intera linea nel buffer, è possibile rimuovere l'input extra fino a quando non si incontra una newline (fgetc in un ciclo). – AusCBloke

+0

'scanf' ha limiti di caratteri, come' scanf ("% 99s", mySize100Buffer); '. Ma a differenza di 'fgets' il limite è specificato da una stringa di cifre, quindi catturare la relazione BUFSIZE-1 implica la generazione di una stringa di formato personalizzata. Un po 'poco pratico ... – HostileFork

0

Usa questo semplice codice

#include"stdio.h" 
#include"stdlib.h" 
main() 
{ 
    int i=0,j=0; 
    char c,buf[100]; 

start: 
    printf("Enter the name:"); 
    while ((c=getchar()) != '\n') 
    { 
      buf[i++]=c; 
      buf[i]='\0'; 
    } 
    if (buf[0] == '\0') 
    { 
    printf("invalid name\n"); 
    goto start; 
    } 
    else 
    printf("Your name is valid\nName = %s\n",buf); 
     }                                
Problemi correlati