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.
você poderia esclarecer, por favor? como @Tim disse abaixo, é confuso o que você está pedindo :)
warren
Respostas:
81
Você precisa de gerenciamento de memória dinâmico e usar a fgetsfunçã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
Advertência - é necessário verificar o resultado do realocamento lá. Mas, se isso falhar, provavelmente haverá problemas piores.
Tim
4
Você provavelmente poderia melhorar um pouco a eficiência fazendo fgets com buffer e verificando se há o caractere de nova linha no final. Do contrário, realoque seu buffer de acumulação, copie-o e fgets novamente.
Paul Tomblin
3
Esta função precisa de uma correção: a linha "len = lenmax;" após o realloc deve preceder o realloc ou deve ser "len = lenmax >> 1;" - ou algum outro equivalente que explique o fato de que metade do comprimento já foi usado.
Matt Gallagher
1
@Johannes, em resposta à sua pergunta, a abordagem de @Paul PODE ser mais rápida na maioria das implementações libc (isto é, reentrantes), uma vez que sua abordagem bloqueia implicitamente stdin para cada personagem enquanto a dele bloqueia uma vez por buffer. Você poderia usar o menos portátil fgetc_unlockedse a segurança do thread não fosse uma preocupação, mas o desempenho, sim.
vladr
3
Observe que este getline() é diferente da getline()função padrão POSIX .
Jonathan Leffler
28
Se estiver usando a biblioteca GNU C ou outra biblioteca compatível com POSIX, você pode usar getline()e passar stdinpara ela para o fluxo de arquivos.
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.
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);return0;}
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;)
Isso não permite cordas longas ... - que eu acho que é o ponto crucial de sua pergunta.
Tim
2
-1, gets () não deve ser usado, pois não faz a verificação de limites.
relaxe
7
Por outro lado, se você está escrevendo um programa para si mesmo e só precisa ler uma entrada, isso está perfeitamente bem. A quantidade de segurança de que um programa precisa faz parte das especificações - você não precisa colocá-la como uma prioridade sempre.
Martin Beckett
4
@Tim - Eu quero manter toda a história :)
Paul Kapustin
4
Votos negativos. getsnão existe mais, portanto, isso não funciona em C11.
Antti Haapala
11
Pode ser necessário usar um loop caractere por caractere (getc ()) para garantir que não haja estouros de buffer e não trunque a entrada.
Qual é o propósito de lenaqui, quando a leitura fornece o comprimento também
Abdul
@Abdul see 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.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
6
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
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).
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 EOFcaractere
finalmente removemos qualquer memória alocada adicional antes de retornar a linha
//the function to read lines of variable lengthchar* scan_line(char*line){int ch;// as getchar() returns `int`long capacity =0;// capacity of the bufferlong length =0;// maintains the length of the stringchar*temp = NULL;// use additional pointer to perform allocations in order to avoid memory leakswhile(((ch = getchar())!='\n')&&(ch != EOF)){if((length +1)>= capacity){// resetting capacityif(capacity ==0)
capacity =2;// some initial fixed length else
capacity *=2;// double the size// try reallocating the memoryif((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 memoryif((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 functionschar* 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}
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
*/typedefenum{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;}
Seu código se tornaria MUITO mais simples se você o usasse gotopara lidar com o caso de erro. No entanto, você não acha que poderia reutilizar tmp_buf, em vez de malloccolocá-lo com o mesmo tamanho repetidamente no loop?
Shahbaz
Usar uma única variável global has_errpara 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.
Jonathan Leffler
0
Em sistemas BSD e Android, você também pode usar fgetln:
O linenã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.
Sim, a função existe. A ressalva de que ele não fornece uma string terminada em nulo é suficientemente grande e problemática que provavelmente é melhor não usá-la - é perigoso.
Jonathan Leffler
0
Algo assim:
unsignedint getConsoleInput(char**pStrBfr)//pass in pointer to char pointer, returns size of buffer{char* strbfr;int c;unsignedint 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 consoleif(strbfr==NULL)goto error;}
strbfr[i]='\0';*pStrBfr = strbfr;//successfully returns pointer to NULL terminated bufferreturn 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);
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:Nota : Nunca use get! Ele não faz verificação de limites e pode estourar o buffer
fonte
fgetc_unlocked
se a segurança do thread não fosse uma preocupação, mas o desempenho, sim.getline()
é diferente dagetline()
função padrão POSIX .Se estiver usando a biblioteca GNU C ou outra biblioteca compatível com POSIX, você pode usar
getline()
e passarstdin
para ela para o fluxo de arquivos.fonte
Uma implementação muito simples, mas insegura para ler a linha para alocação estática:
Uma implementação mais segura, sem a possibilidade de estouro de buffer, mas com a possibilidade de não ler a linha inteira, é:
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.
fonte
gets
foi removido do padrão completamentePortanto, se você estiver procurando por argumentos de comando, dê uma olhada na resposta de Tim. Se você quiser apenas ler uma linha do console:
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;)
fonte
gets
não existe mais, portanto, isso não funciona em C11.Pode ser necessário usar um loop caractere por caractere (getc ()) para garantir que não haja estouros de buffer e não trunque a entrada.
fonte
getline
exemplo executávelMencionado 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 *"?
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 simplesgetline
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.
fonte
len
aqui, quando a leitura fornece o comprimento tambémman 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 queread
.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:
fonte: https://www.tutorialspoint.com/c_standard_library/c_function_fgets.htm
fonte
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:
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).
fonte
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'
ouEOF
caracterefinalmente removemos qualquer memória alocada adicional antes de retornar a linha
Agora você pode ler uma linha completa desta forma:
Aqui está um programa de exemplo usando a
scan_line()
função:amostra de entrada:
amostra de saída:
fonte
Me deparei com o mesmo problema há algum tempo, essa foi minha solução, espero que ajude.
fonte
goto
para lidar com o caso de erro. No entanto, você não acha que poderia reutilizartmp_buf
, em vez demalloc
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
:Igual a:
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.fonte
Algo assim:
fonte
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.
}
fonte
Esta função deve fazer o que você quiser:
Eu espero que isso ajude.
fonte
fgets( buffer, sizeof(buffer), file );
não deveriasizeof(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.