Pregunta

¿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 suponer nada sobre su contenido.

¿Fue útil?

Solución

Necesita una 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. Entonces usas 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 : ¡Nunca use gets! No verifica los límites y puede desbordar el búfer

Otros consejos

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

Una implementación muy simple pero insegura para leer la línea para la asignación estática:

char line[1024];

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

Una implementación más segura, sin la posibilidad de desbordamiento del 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 la cadena de formato. Es un artefacto histórico.

Es posible que deba usar un bucle de carácter por carácter (getc ()) para asegurarse de que no haya desbordamientos de búfer y no truncar la entrada.

Entonces, si estaba buscando argumentos de comando, eche un vistazo a la respuesta de Tim. Si solo quieres 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 desbordar el búfer, no comprueba el final del archivo, no admite codificaciones y muchas otras cosas. En realidad, ni siquiera pensé si hiciera CUALQUIERA de estas cosas. Estoy de acuerdo en que me equivoqué :) Pero ... cuando veo una pregunta como "¿Cómo leer una línea desde la consola en C?", Supongo que una persona necesita algo simple, como gets () y no 100 líneas de código como las anteriores. De hecho, creo que si intentas escribir esas 100 líneas de código en realidad, cometerías muchos más errores de los que hubieras hecho si hubieras elegido get;)

getline ejemplo ejecutable

Mencionó en esta respuesta pero aquí hay un ejemplo.

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

Punteros nuevos, lea esto: ¿Por qué el primer argumento de getline es un puntero al puntero " char ** " en lugar de " 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;
}

implementación de glibc

¿Sin POSIX? Tal vez quiera mirar el implementación de glibc 2.23 .

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

Duplica la memoria asignada cada vez que se necesita un aumento y parece segura para subprocesos.

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

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. Puede crecer dinámicamente el búfer si no puede establecer un tamaño de línea máximo razonable.

También puede usar fgets como una forma segura de obtener una línea como una cadena terminada en C nula:

#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 ha agotado la entrada de la consola o si la operación falló por algún motivo, se devuelve eof == NULL y el búfer de línea puede no modificarse (por lo que es útil configurar el primer carácter en '\ 0').

fgets no llenará en exceso la línea [] y asegurará que haya un valor nulo después del último carácter aceptado en un retorno exitoso.

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

Si no hay una terminación '\ n' antes de la terminación '\ 0', es posible que haya más datos o que la próxima solicitud informará el final del archivo. Tendrás que hacer otros fgets para determinar cuál es cuál. (En este sentido, hacer bucles 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 fgets exitosos, sabe que el búfer se llenó por completo. Si esa posición es seguida por un '\ n', sabe que tuvo suerte. De lo contrario, hay más datos o un final de archivo más adelante en stdin. (Cuando el búfer no se llena por completo, aún podría estar al final del archivo y es posible que no haya 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 primer '\ 0' en el búfer), me inclino a preferir usar getchar () en primer lugar.)

Haz lo que necesites hacer para lidiar con que todavía haya más líneas que la cantidad que lees como el primer fragmento. Los ejemplos de crecimiento dinámico de un búfer se pueden hacer funcionar con getchar o fgets. Hay algunos casos extremos difíciles de controlar (como recordar que la próxima entrada comience a almacenarse en la posición del '\ 0' que finalizó la entrada anterior antes de que se extendiera el búfer).

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

Si conoce la longitud de antemano, intente 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

  

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

  • Construir su propia función es una de las formas que lo ayudaría a lograr leer una línea desde la consola en C .

  • Estoy usando asignación de memoria dinámica para asignar la cantidad suficiente de memoria necesaria para contener 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 uno por uno usando la función getchar () hasta que el usuario ingrese '\ n' o 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 puedes leer una línea completa de esta manera:

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

Aquí hay un programa de ejemplo que utiliza la función scan_line () :

#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
}

entrada de muestra:

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

salida de muestra:

<*>

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;
}

En sistemas BSD y Android también puede usar fgetln :

#include <stdio.h>

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

Me gusta así:

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

La línea no tiene terminación nula y contiene \ n (o lo que sea que esté usando su plataforma) al final. Se vuelve inválido después de la próxima operación de E / S en la transmisión.

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;
}

Esta función debe hacer lo que quieras:

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.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top