Qual é a maneira mais simples de ler uma linha inteira em um programa de console C? O texto inserido pode ter um comprimento variável e não podemos fazer nenhuma suposição sobre seu conteúdo.
Qual é a maneira mais simples de ler uma linha inteira em um programa de console C? O texto inserido pode ter um comprimento variável e não podemos fazer nenhuma suposição sobre seu conteúdo.
Respostas:
Você precisa de gerenciamento de memória dinâmico e usar a fgets
função para ler sua linha. No entanto, parece não haver maneira de ver quantos caracteres ele lê. Então você usa 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! Ele não faz verificação de limites e pode estourar o buffer
fgetc_unlocked
se a segurança do thread não fosse uma preocupação, mas o desempenho, sim.
getline()
é diferente da getline()
função padrão POSIX .
Uma implementação muito simples, mas insegura para ler a linha para alocação estática:
char line[1024];
scanf("%[^\n]", line);
Uma implementação mais segura, sem a possibilidade de estouro de buffer, mas com a possibilidade de não ler a linha inteira, é:
char line[1024];
scanf("%1023[^\n]", line);
Não a 'diferença por um' entre o comprimento especificado declarando a variável e o comprimento especificado na string de formato. É um artefato histórico.
gets
foi removido do padrão completamente
Portanto, se você estiver procurando por argumentos de comando, dê uma olhada na resposta de Tim. Se você quiser apenas ler uma linha do console:
#include <stdio.h>
int main()
{
char string [256];
printf ("Insert your full address: ");
gets (string);
printf ("Your address is: %s\n",string);
return 0;
}
Sim, não é seguro, você pode fazer buffer overrun, não verifica o fim do arquivo, não suporta codificações e muitas outras coisas. Na verdade, eu nem pensei se ele fazia alguma dessas coisas. Concordo que estraguei tudo :) Mas ... quando vejo uma pergunta como "Como ler uma linha do console em C?", Presumo que uma pessoa precise de algo simples, como gets () e não 100 linhas de código como acima. Na verdade, eu acho, se você tentar escrever essas 100 linhas de código na realidade, você cometerá muito mais erros do que teria feito se tivesse escolhido get;)
gets
não existe mais, portanto, isso não funciona em C11.
getline
exemplo executável
Mencionado nesta resposta mas aqui está um exemplo.
É POSIX 7 , aloca memória para nós e reutiliza o buffer alocado em um loop.
Ponteiro newbs, leia isto: Por que o primeiro argumento de getline é um ponteiro para o ponteiro "char **" em vez 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;
}
implementação glibc
Sem POSIX? Talvez você queira dar uma olhada na implementação da glibc 2.23 .
Resolve para getdelim
, que é um superconjunto POSIX simples getline
com um terminador de linha arbitrário.
Ele dobra a memória alocada sempre que um aumento é necessário e parece seguro para threads.
Requer alguma expansão macro, mas é improvável que você faça muito melhor.
len
aqui, quando a leitura fornece o comprimento também
man getline
. len
é o comprimento do buffer existente, 0
é mágico e diz a ele para alocar. Ler é o número de caracteres lidos. O tamanho do buffer pode ser maior que read
.
Muitas pessoas, como eu, chegam a este post com o título correspondendo ao que é pesquisado, embora a descrição diga sobre comprimento variável. Na maioria dos casos, sabemos o comprimento de antemão.
Se você sabe o comprimento de antemão, tente abaixo:
char str1[1001] = { 0 };
fgets(str1, 1001, stdin); // 1000 chars may be read
fonte: https://www.tutorialspoint.com/c_standard_library/c_function_fgets.htm
Conforme sugerido, você pode usar getchar () para ler do console até que um fim de linha ou um EOF seja retornado, construindo seu próprio buffer. O crescimento do buffer pode ocorrer dinamicamente se você não puder definir um tamanho de linha máximo razoável.
Você também pode usar o uso de fgets como uma maneira segura de obter uma linha como uma string C terminada em 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);
Se você esgotou a entrada do console ou se a operação falhou por algum motivo, eof == NULL é retornado e o buffer de linha pode estar inalterado (é por isso que definir o primeiro caractere como '\ 0' é útil).
fgets não irá sobrecarregar a linha [] e garantirá que haja um nulo após o último caractere aceito em um retorno bem-sucedido.
Se o fim da linha for atingido, o caractere precedendo o final '\ 0' será um '\ n'.
Se não houver término '\ n' antes do término '\ 0', pode ser que haja mais dados ou que a próxima solicitação relatará o fim do arquivo. Você terá que fazer outro fgets para determinar qual é qual. (Nesse sentido, fazer um loop com getchar () é mais fácil.)
No código de exemplo (atualizado) acima, se linha [sizeof (linha) -1] == '\ 0' após fgets bem-sucedidos, você sabe que o buffer foi preenchido completamente. Se essa posição for precedida por um '\ n', você sabe que teve sorte. Caso contrário, haverá mais dados ou um fim de arquivo adiante em stdin. (Quando o buffer não está completamente preenchido, você ainda pode estar no final do arquivo e também pode não haver um '\ n' no final da linha atual. Já que você tem que examinar a string para encontrar e / ou elimine qualquer '\ n' antes do final da string (o primeiro '\ 0' no buffer), estou inclinado a preferir usar getchar () em primeiro lugar.)
Faça o que for necessário para lidar com o fato de ainda haver mais linha do que a quantidade que você leu como o primeiro pedaço. Os exemplos de crescimento dinâmico de um buffer podem funcionar com getchar ou fgets. Existem alguns casos extremos complicados a serem observados (como lembrar que a próxima entrada começa a ser armazenada na posição '\ 0' que encerrou a entrada anterior antes que o buffer fosse estendido).
Como ler uma linha do console em C?
Construir sua própria função é uma das maneiras que o ajudaria a conseguir ler uma linha do console
Estou usando a alocação de memória dinâmica para alocar a quantidade necessária de memória necessária
Quando estamos prestes a esgotar a memória alocada, tentamos dobrar o tamanho da memória
E aqui estou usando um loop para verificar cada caractere da string, um por um, usando a getchar()
função até que o usuário insira '\n'
ou EOF
caractere
finalmente removemos qualquer memória alocada adicional antes de retornar a linha
//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;
}
Agora você pode ler uma linha completa desta forma:
char *line = NULL;
line = scan_line(line);
Aqui está um programa de exemplo usando a scan_line()
função:
#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
}
amostra de entrada:
Twinkle Twinkle little star.... in the sky!
amostra de saída:
Twinkle Twinkle little star.... in the sky!
Me deparei com o mesmo problema há algum tempo, essa foi minha solução, espero que ajude.
/*
* 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 lidar com o caso de erro. No entanto, você não acha que poderia reutilizar tmp_buf
, em vez de malloc
colocá-lo com o mesmo tamanho repetidamente no loop?
has_err
para relatar erros torna essa função thread-insegura e menos confortável de usar. Não faça assim. Você já indica um erro ao retornar NULL. Também há espaço para pensar que as mensagens de erro impressas não são uma boa ideia em uma função de biblioteca de uso geral.
Em sistemas BSD e Android, você também pode usar fgetln
:
#include <stdio.h>
char *
fgetln(FILE *stream, size_t *len);
Igual a:
size_t line_len;
const char *line = fgetln(stdin, &line_len);
O line
não é terminado em null e contém \n
(ou o que quer que sua plataforma esteja usando) no final. Torna-se inválido após a próxima operação de E / S no fluxo.
Algo assim:
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;
}
A melhor e mais simples maneira de ler uma linha de um console é usando a função getchar (), por meio da qual você armazenará um caractere por vez em um array.
{
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 função deve fazer o que você quiser:
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 );
Eu espero que isso ajude.
fgets( buffer, sizeof(buffer), file );
não deveria sizeof(buffer)-1
. fgets
deixa espaço para o nulo final.
while (!feof(file))
está sempre errado e este é apenas mais um exemplo de uso incorreto.