2008-11-24 44 views
89

Cuál es la forma más sencilla de leer una línea completa en un programa de consola C El texto ingresado puede tener una longitud variable y no podemos hacer ninguna suposición sobre su contenido.¿Cómo leer una línea desde la consola en C?

+0

podría aclarar, por favor? como @Tim dice a continuación, confunde lo que estás pidiendo :) – warren

Respuesta

67

Necesita gestión de memoria dinámica, y utilice la función fgets para leer su línea. Sin embargo, parece que no hay forma de ver cuántos caracteres lee. Por lo que utilizar fgetc:

char * getline(void) { 
    char * line = malloc(100), * linep = line; 
    size_t lenmax = 100, len = lenmax; 
    int c; 

    if(line == NULL) 
     return NULL; 

    for(;;) { 
     c = fgetc(stdin); 
     if(c == EOF) 
      break; 

     if(--len == 0) { 
      len = lenmax; 
      char * linen = realloc(linep, lenmax *= 2); 

      if(linen == NULL) { 
       free(linep); 
       return NULL; 
      } 
      line = linen + (line - linep); 
      linep = linen; 
     } 

     if((*line++ = c) == '\n') 
      break; 
    } 
    *line = '\0'; 
    return linep; 
} 

Nota: No utilice nunca se pone! No hace la comprobación de límites y puede desbordar su memoria intermedia

+1

disculpa las numerosas ediciones de ese código. como ves, hay muchas trampas :) –

+0

Advertencia: hay que verificar el resultado de realloc allí. Pero si eso falla, entonces es probable que haya problemas peores. – Tim

+0

a la derecha. no es "excepción segura" :) –

8

Por lo tanto, si buscaba argumentos de comando, eche un vistazo a la respuesta de Tim. Si lo que desea es leer una línea desde la consola:

#include <stdio.h> 

int main() 
{ 
    char string [256]; 
    printf ("Insert your full address: "); 
    gets (string); 
    printf ("Your address is: %s\n",string); 
    return 0; 
} 

Sí, no es seguro, puede hacerlo de saturación de búfer, que no comprueba el final del archivo, no es compatible con las codificaciones y mucho de otras cosas. En realidad, ni siquiera pensé si era CUALQUIERA de estas cosas. Acepto que me equivoqué :) Pero ... cuando veo una pregunta como "¿Cómo leer una línea de la consola en C?", Supongo que una persona necesita algo simple, como get() y no 100 líneas del código como arriba. En realidad, creo que, si intenta escribir esas 100 líneas de código en la realidad, que haría muchos más errores, de lo que hubiera hecho si hubiera escogido recibe;)

+0

Esto no permite cadenas largas ... - que creo que es el quid de su pregunta. – Tim

+1

-1, get() no se debe utilizar, ya que no hace la comprobación de límites. – unwind

+0

obtiene es una función segura – Baget

11

puede que tenga que utilizar un carácter de el bucle de caracteres (getc()) para garantizar que no tenga desbordamientos de búfer y no trunque la entrada.

+3

¿cómo podría alguien llamar esto ofensivo o rechazar esta respuesta? Guau. la respuesta aceptada usa getc(). Alguien debe estar molesto conmigo por otra pregunta, apuesto. – Tim

+2

Acepto que un -1 es un poco exagerado, agregando +1 para obtener 0 (que es el valor correcto en mi opinión). – hlovdal

+0

Agregué -1, porque esto no agrega nada a otras respuestas; es una respuesta parcial en el mejor de los casos, aunque más como un comentario. –

-3

Esta función debe hacer lo que quiera:

char* readLine(FILE* file) 
{ 
char buffer[1024]; 
char* result = 0; 
int length = 0; 

while(!feof(file)) 
    { 
    fgets(buffer, sizeof(buffer), file); 
    int len = strlen(buffer); 
    buffer[len] = 0; 

    length += len; 
    char* tmp = (char*)malloc(length+1); 
    tmp[0] = 0; 

    if(result) 
    { 
    strcpy(tmp, result); 
    free(result); 
    result = tmp; 
    } 

    strcat(result, buffer); 

    if(strstr(buffer, "\n") break; 
    } 

return result; 
} 

char* line = readLine(stdin); 
/* Use it */ 
free(line); 

espero que esto ayude.

+1

Deberías hacer 'fgets (buffer, sizeof (buffer), file);' not 'sizeof (buffer) -1'. 'fgets' deja espacio para el nulo de terminación. – user102008

+0

cierto, gracias por el lugar. –

0

que cambiaría @ respuesta de litb a

char *getline(void) 
{ 
    char* accumulator = malloc(100); 
    char readBuf[100]; 
    int accumulatorSize = 100; 
    *accumulator = '\0'; 

    while (!feof(stdin)) 
    { 
    fgets(readBuf, 99, stdin); 
    strcat(accumulator, readBuf); 
    /* possible fencepost error here */ 
    if (readBuf[strlen(readBuf)] != '\n') 
    { 
     accumulatorSize += 100; 
     accumulator = realloc(accumulator, accumulatorSize); 
     /* should probably check for realloc returning null */ 
    } 
    else 
     break; 
    } 
    return accumulator; 
} 

Tenga en cuenta que es el código C más desnuda de lo que he escrito en 11 años.

+0

Esto pierde la variable readBuf –

+0

@Adam - hace que readBuf sea una matriz automática. Eso debería detener la fuga. –

+0

Paul, este código tiene otros problemas :) por ejemplo, se compara un char con un literal de cadena :) (control fencepost) también se vuelve fgets devuelve redBuf (char *), no es un número entero que se puede comparar con EOF :) –

20

Si está utilizando la biblioteca C de GNU u otra biblioteca compatible con POSIX, puede usar getline() y pasarle stdin para la secuencia de archivos.

+0

Y es POSIX 7 http://pubs.opengroup.org/onlinepubs/9699919799/functions/getdelim.html –

0

Me encontré con el mismo problema hace algún tiempo, esta fue mi solución, espero que ayude.

/* 
* Initial size of the read buffer 
*/ 
#define DEFAULT_BUFFER 1024 

/* 
* Standard boolean type definition 
*/ 
typedef enum{ false = 0, true = 1 }bool; 

/* 
* Flags errors in pointer returning functions 
*/ 
bool has_err = false; 

/* 
* Reads the next line of text from file and returns it. 
* The line must be free()d afterwards. 
* 
* This function will segfault on binary data. 
*/ 
char *readLine(FILE *file){ 
    char *buffer = NULL; 
    char *tmp_buf = NULL; 
    bool line_read = false; 
    int iteration = 0; 
    int offset = 0; 

    if(file == NULL){ 
     fprintf(stderr, "readLine: NULL file pointer passed!\n"); 
     has_err = true; 

     return NULL; 
    } 

    while(!line_read){ 
     if((tmp_buf = malloc(DEFAULT_BUFFER)) == NULL){ 
      fprintf(stderr, "readLine: Unable to allocate temporary buffer!\n"); 
      if(buffer != NULL) 
       free(buffer); 
      has_err = true; 

      return NULL; 
     } 

     if(fgets(tmp_buf, DEFAULT_BUFFER, file) == NULL){ 
      free(tmp_buf); 

      break; 
     } 

     if(tmp_buf[strlen(tmp_buf) - 1] == '\n') /* we have an end of line */ 
      line_read = true; 

     offset = DEFAULT_BUFFER * (iteration + 1); 

     if((buffer = realloc(buffer, offset)) == NULL){ 
      fprintf(stderr, "readLine: Unable to reallocate buffer!\n"); 
      free(tmp_buf); 
      has_err = true; 

      return NULL; 
     } 

     offset = DEFAULT_BUFFER * iteration - iteration; 

     if(memcpy(buffer + offset, tmp_buf, DEFAULT_BUFFER) == NULL){ 
      fprintf(stderr, "readLine: Cannot copy to buffer\n"); 
      free(tmp_buf); 
      if(buffer != NULL) 
       free(buffer); 
      has_err = true; 

      return NULL; 
     } 

     free(tmp_buf); 
     iteration++; 
    } 

    return buffer; 
} 
+1

Su código sería MUCHO más simple si usa 'goto' para manejar el caso de error. Sin embargo, ¿no crees que podrías reutilizar 'tmp_buf', en lugar de' malloc'ing con el mismo tamaño una y otra vez en el ciclo? – Shahbaz

10

Un muy simple, pero su aplicación insegura para leer la línea para la asignación estática:

char line[1024]; 

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

Una aplicación más segura, sin posibilidad de desbordamiento de búfer, pero con la posibilidad de no leer toda la línea, es :

char line[1024]; 

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

No es la "diferencia en uno" entre la longitud especificada que declara la variable y la longitud especificada en el formato de cadena. Es un artefacto histórico.

+13

** Esto no es seguro en absoluto. Sufre exactamente el mismo problema por el que 'get' se eliminó por completo del estándar ** –

+0

** Nota del moderador: ** El comentario anterior se refiere a una [revisión anterior de la respuesta.] (Https: // stackoverflow.com/posts/318799/reviews) –

4

Como se sugiere, puede usar getchar() para leer desde la consola hasta que se devuelva un final de línea o un EOF, creando su propio búfer. El búfer en crecimiento puede ocurrir dinámicamente si no puede establecer un tamaño de línea máximo razonable.

Se puede utilizar también utilizar fgets como una forma segura de obtener una línea como un C terminada en nulo cadena:

#include <stdio.h> 

char line[1024]; /* Generously large value for most situations */ 

char *eof; 

line[0] = '\0'; /* Ensure empty line if no input delivered */ 
line[sizeof(line)-1] = ~'\0'; /* Ensure no false-null at end of buffer */ 

eof = fgets(line, sizeof(line), stdin); 

Si no encuentra la entrada de la consola o si la operación ha fallado por alguna razón, EF == Se devuelve NULL y el buffer de línea puede no haber cambiado (por eso es útil configurar el primer carácter en '\ 0').

fgets no sobrecargará la línea [] y se asegurará de que haya un valor nulo después del último carácter aceptado en una devolución exitosa.

Si se ha alcanzado el final de línea, el carácter que precede a la terminación '\ 0' será '\ n'.

Si no hay una terminación '\ n' antes de la terminación '\ 0' puede ser que haya más datos o que la próxima solicitud informe de fin de archivo. Tendrás que hacer otros errores para determinar cuál es cuál. (En este sentido, el bucle con getchar() es más fácil.)

En el código de ejemplo (actualizado) anterior, si la línea [sizeof (línea) -1] == '\ 0' después de los cambios exitosos, sabe que el buffer fue llenado completamente Si esa posición es procesada por un '\ n', sabrá que tuvo suerte. De lo contrario, hay más datos o un final de archivo por delante en stdin. (Cuando el búfer no se llena por completo, aún podría estar en un final de archivo y también podría no haber un '\ n' al final de la línea actual. Ya que debe escanear la cadena para encontrar y/o eliminar cualquier '\ n' antes del final de la cadena (el primero '\ 0' en el buffer), prefiero usar getchar() en primer lugar.)

Haga lo que necesite hacer para hacer frente a que todavía hay más líneas que la cantidad que lees como primer trozo. Los ejemplos de un buffer de crecimiento dinámico se pueden hacer para trabajar con getchar o con fgets. Hay algunos casos de bordes complicados a tener en cuenta (como recordar que el siguiente inicio de entrada se almacena en la posición del '\ 0' que finalizó la entrada anterior antes de que se ampliara el búfer).

0

En sistemas BSD y Android también se puede utilizar fgetln:

#include <stdio.h> 

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

así:

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

El line no está terminada en nulo y contiene \n (o lo que su plataforma está utilizando) en el fin. Se vuelve inválido después de la siguiente operación de E/S en la transmisión.

2

Muchas personas, como yo, llegan a esta publicación con el título que coincide con lo que se busca, aunque la descripción se refiere a la longitud variable. Para la mayoría de los casos, sabemos la longitud de antemano.

Si lo hace saber la longitud antes de la mano, tratar a continuación:

char str1[1001] = { 0 }; 
fgets(str1, 1001, stdin); // 1000 chars may be read 

fuente: https://www.tutorialspoint.com/c_standard_library/c_function_fgets.htm

8

getline ejemplo ejecutable

mencionado on this answer pero aquí es un ejemplo.

Es POSIX 7, nos asigna memoria, y reutiliza el búfer asignado en un bucle muy bien.

newbs Pointer, lee esto: Why is the first argument of getline a pointer to pointer "char**" instead of "char*"?

#define _XOPEN_SOURCE 700 

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

int main(void) { 
    char *line = NULL; 
    size_t len = 0; 
    ssize_t read = 0; 
    while (read != -1) { 
     puts("enter a line"); 
     read = getline(&line, &len, stdin); 
     printf("line = %s", line); 
     printf("line length = %zu\n", read); 
     puts(""); 
    } 
    free(line); 
    return 0; 
} 

aplicación glibc

Sin POSIX? Tal vez quiera mirar el glibc 2.23 implementation.

Se resuelve en getdelim, que es un superconjunto POSIX simple de getline con un terminador de línea arbitrario.

Dobla la memoria asignada cada vez que se necesita aumentar, y se ve seguro para la ejecución de subprocesos.

Requiere algo de expansión macro, pero es poco probable que lo haga mucho mejor.

+1

más uno para "newbs de puntero". – marcusshep

+0

¿Cuál es el propósito de 'len' aquí, cuando read proporciona la longitud también – Abdul

+0

@Abdul see' man getline'. 'len' es la longitud del búfer existente,' 0' es mágico y le dice que lo asigne. Leer es el número de caracteres leídos. El tamaño del buffer puede ser mayor que 'leer'. –

0

¿Cómo leer una línea desde la consola en C?

  • La construcción de su propia función, es uno de la manera que le ayudaría a lograr la lectura de una línea desde la consola en C.

  • Estoy usando dynamic memory allocation para asignar la cantidad suficiente de memoria necesaria para mantener todos los caracteres de una línea junto con el carácter '\0'.

  • Y aquí estoy usando un bucle para escanear cada carácter de la cadena de uno en uno utilizando la función getchar() hasta que el usuario entra o '\n'EOF carácter

    //the function to read lines of variable length 
    
    char* scan_line(char *line) 
    { 
        int ch; //as getchar() returns `int` 
    
        if((line = malloc(sizeof(char))) == NULL) //allocating memory 
        { 
         //checking if allocation was successful or not 
         printf("unsuccessful allocation"); 
         exit(1); 
        } 
    
        line[0]='\0'; 
    
        for(int index = 0; ((ch = getchar())!='\n') && (ch != EOF) ; index++) 
        { 
         if((line = realloc(line, (index + 2)*sizeof(char))) == NULL) 
         { 
          //checking if reallocation was successful or not 
          printf("unsuccessful reallocation"); 
          exit(1); 
         } 
    
         line[index] = (char) ch; //type casting `int` to `char` 
         line[index + 1] = '\0'; //inserting null character at the end 
        } 
    
        return line; 
    } 
    
  • Ahora se podía leer una línea completa de esta manera:

    char *line = NULL; 
    line = scan_line(line); 
    

He aquí un programa de ejemplo usando la función scan_line(): entrada

#include <stdio.h> 
#include <stdlib.h> //for dynamic allocation functions 

char* scan_line(char *line) 
{ 
    .......... 
} 

int main(void) 
{ 
    char *a = NULL; 

    a = scan_line(a); //function call to scan the line 

    printf("%s\n",a); //printing the scanned line 

    free(a); //don't forget to free the malloc'd pointer 
} 

de ejemplo: salida de

Twinkle Twinkle little star.... in the sky! 

muestra:

Twinkle Twinkle little star.... in the sky! 
0

Algo como esto:

unsigned int getConsoleInput(char **pStrBfr) //pass in pointer to char pointer, returns size of buffer 
{ 
    char * strbfr; 
    int c; 
    unsigned int i; 
    i = 0; 
    strbfr = (char*)malloc(sizeof(char)); 
    if(strbfr==NULL) goto error; 
    while((c = getchar()) != '\n' && c != EOF) 
    { 
     strbfr[i] = (char)c; 
     i++; 
     strbfr = (void*)realloc((void*)strbfr,sizeof(char)*(i+1)); 
     //on realloc error, NULL is returned but original buffer is unchanged 
     //NOTE: the buffer WILL NOT be NULL terminated since last 
     //chracter came from console 
     if(strbfr==NULL) goto error; 
    } 
    strbfr[i] = '\0'; 
    *pStrBfr = strbfr; //successfully returns pointer to NULL terminated buffer 
    return i + 1; 
    error: 
    *pStrBfr = strbfr; 
    return i + 1; 
} 
Cuestiones relacionadas