¿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.
¿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.
Respuestas:
Necesita administración de memoria dinámica y usa la fgets
función para leer su línea. Sin embargo, parece que no hay forma de saber 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 get! No comprueba los límites y puede desbordar su búfer
fgetc_unlocked
si la seguridad de los subprocesos no es una preocupación, pero el rendimiento sí.
getline()
es diferente de la getline()
función estándar POSIX .
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 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.
gets
se eliminó por completo del estándar
Entonces, si estaba buscando argumentos de comando, eche un vistazo a la respuesta de Tim. Si solo desea leer una línea de 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 saturar el búfer, no verifica el final del archivo, no admite codificaciones y muchas otras cosas. En realidad, ni siquiera pensé si hacía ALGUNA de estas cosas. Estoy de acuerdo, lo arruiné un poco :) Pero ... cuando veo una pregunta como "¿Cómo leer una línea de la consola en C?", Asumo que una persona necesita algo simple, como gets () y no 100 líneas de código. como arriba. En realidad, creo que si intenta escribir esas 100 líneas de código en realidad, cometería muchos más errores de los que hubiera cometido si hubiera elegido get;)
gets
ya no existe, por lo que esto no funciona en C11.
getline
ejemplo ejecutable
Mencionado 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.
Pointer newbs, 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 glibc
¿Sin POSIX? Tal vez quiera ver la implementación glibc 2.23 .
Se resuelve en getdelim
, que es un superconjunto POSIX simple getline
con un terminador de línea arbitrario.
Duplica la memoria asignada cada vez que se necesita un aumento y parece seguro para subprocesos.
Requiere cierta expansión macro, pero es poco probable que lo haga mucho mejor.
len
aquí, cuando se lee también proporciona la longitud
man getline
. len
es la longitud del búfer existente, 0
es mágico y le dice que asigne. Leer es el número de caracteres leídos. El tamaño del búfer podría ser mayor que read
.
Muchas personas, como yo, llegan a esta publicación con el título que coincide con lo que se busca, aunque la descripción dice sobre la longitud variable. En la mayoría de los casos, conocemos la longitud de antemano.
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
Como se sugirió, 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 producirse un crecimiento de búfer dinámicamente 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 C terminada en nulo:
#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 alguna razón, se devuelve eof == NULL y es posible que el búfer de línea no se modifique (por lo que establecer el primer carácter en '\ 0' es útil).
fgets no sobrecargará la línea [] y se asegurará de que haya un nulo después del último carácter aceptado en una devolución exitosa.
Si se alcanzó el final de la línea, el carácter que precede al '\ 0' final será un '\ n'.
Si no hay terminación '\ n' antes de la terminación '\ 0', es posible que haya más datos o que la siguiente solicitud informe el final del archivo. Tendrá que hacer otros fgets para determinar cuál es cuál. (En este sentido, hacer un 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 fgets exitosos, sabrá que el búfer se llenó por completo. Si esa posición está precedida por un '\ n', sabrá 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, es posible que aún esté al final del archivo y que tampoco haya un '\ n' al final de la línea actual. Dado 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).
Haga lo que tenga que hacer para lidiar con que todavía haya más línea que la cantidad que leyó como el primer fragmento. Los ejemplos de crecimiento dinámico de un búfer se pueden hacer para que funcionen con getchar o fgets. Hay algunos casos extremos complicados a tener en cuenta (como recordar que la siguiente entrada comience a almacenarse en la posición del '\ 0' que terminó la entrada anterior antes de que se extendiera el búfer).
¿Cómo leer una línea de la consola en C?
Construir su propia función es una de las formas que le ayudaría a lograr leer una línea desde la consola
Estoy usando la asignación de memoria dinámica para asignar la cantidad requerida de memoria requerida
Cuando estamos a punto de agotar la memoria asignada, intentamos duplicar el tamaño de la memoria.
Y aquí estoy usando un bucle para escanear cada carácter de la cadena uno por uno usando la getchar()
función hasta que el usuario ingrese '\n'
o EOF
carácter
finalmente eliminamos cualquier memoria asignada adicionalmente antes de devolver la línea
//the function to read lines of variable length
char* scan_line(char *line)
{
int ch; // as getchar() returns `int`
long capacity = 0; // capacity of the buffer
long length = 0; // maintains the length of the string
char *temp = NULL; // use additional pointer to perform allocations in order to avoid memory leaks
while ( ((ch = getchar()) != '\n') && (ch != EOF) )
{
if((length + 1) >= capacity)
{
// resetting capacity
if (capacity == 0)
capacity = 2; // some initial fixed length
else
capacity *= 2; // double the size
// try reallocating the memory
if( (temp = realloc(line, capacity * sizeof(char))) == NULL ) //allocating memory
{
printf("ERROR: unsuccessful allocation");
// return line; or you can exit
exit(1);
}
line = temp;
}
line[length] = (char) ch; //type casting `int` to `char`
}
line[length + 1] = '\0'; //inserting null character at the end
// remove additionally allocated memory
if( (temp = realloc(line, (length + 1) * sizeof(char))) == NULL )
{
printf("ERROR: unsuccessful allocation");
// return line; or you can exit
exit(1);
}
line = temp;
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 usa la scan_line()
función:
#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:
Twinkle Twinkle little star.... in the sky!
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;
}
goto
para manejar el caso de error. Sin embargo, ¿no cree que podría reutilizarlo tmp_buf
, en lugar de malloc
hacerlo con el mismo tamaño una y otra vez en el ciclo?
has_err
para informar errores hace que esta función sea insegura y menos cómoda de usar. No lo hagas de esa manera. Ya indicaste un error al devolver NULL. También hay espacio para pensar que los mensajes de error impresos no son una buena idea en una función de biblioteca de propósito general.
En sistemas BSD y Android también puede usar fgetln
:
#include <stdio.h>
char *
fgetln(FILE *stream, size_t *len);
Al igual que:
size_t line_len;
const char *line = fgetln(stdin, &line_len);
El line
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 siguiente operación de E / S en curso.
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;
}
La mejor y más sencilla forma de leer una línea desde una consola es usar la función getchar (), mediante la cual almacenará un carácter a la vez en una matriz.
{
char message[N]; /* character array for the message, you can always change the character length */
int i = 0; /* loop counter */
printf( "Enter a message: " );
message[i] = getchar(); /* get the first character */
while( message[i] != '\n' ){
message[++i] = getchar(); /* gets the next character */
}
printf( "Entered message is:" );
for( i = 0; i < N; i++ )
printf( "%c", message[i] );
return ( 0 );
}
Esta función debería 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.
fgets( buffer, sizeof(buffer), file );
no sizeof(buffer)-1
. fgets
deja espacio para el nulo de terminación.
while (!feof(file))
siempre está mal y este es solo un ejemplo más de uso erróneo.