Função fgets [RESOLVIDO]

1. Função fgets [RESOLVIDO]

ian cléver sales fernandes
ianclever

(usa Arch Linux)

Enviado em 16/09/2013 - 12:18h

gente é o seguinte, eu já estou de saco cheio de ter que criar uma variável(vetor) string, para depois ter que ler com fgets, ou getchar, ou gets, ou qualquer outra coisa assim, então quero dar uma melhorada nessa função e fazẽ-la alocar isso dinâmicamente em tempo de execução, mas para não precisar reinventar a roda eu quero melhorar da parte que já existe, eu quero saber se alguém tem o código completo da função fgets, já que na biblioteca stdio só tem o protótipo, tá la assim:

 extern char *fgets (char *__restrict __s, int __n, FILE *__restrict __stream); 


onde eu acho a função completa? alguma dica?


  


2. MELHOR RESPOSTA

Paulo
paulo1205

(usa Ubuntu)

Enviado em 17/09/2013 - 05:41h

ianclever escreveu:

gente é o seguinte, eu já estou de saco cheio de ter que criar uma variável(vetor) string, para depois ter que ler com fgets, ou getchar, ou gets, ou qualquer outra coisa assim, então quero dar uma melhorada nessa função e fazẽ-la alocar isso dinâmicamente em tempo de execução, mas para não precisar reinventar a roda eu quero melhorar da parte que já existe, eu quero saber se alguém tem o código completo da função fgets, já que na biblioteca stdio só tem o protótipo, tá la assim:

 extern char *fgets (char *__restrict __s, int __n, FILE *__restrict __stream); 


onde eu acho a função completa? alguma dica?


Se você quer uma função que aloque memória dinamicamente na entrada, pode usar, no Linux, a própria scanf(), pois a GNU LibC (glibc) implementa uma extensão que faz a alocação de ponteiros de caracteres na leitura de strings, por meio do modificador "m", aplicado às conversões "%s" ou "%[...]".

O exemplo abaixo deve lhe dar uma ideia de como fazer uma leitura genérica de uma string ocupando uma linha inteira, inclusive mostrando como tratar possíveis entradas inesperadas.

char *line_ptr;
int n, a, b;

a=b=-1;
n=scanf("%m[^\n]%n%*1[\n]%n", &line_ptr, &a, &b);

if(n==1){
/*
Leitura bem sucedida de uma string.

line_ptr recebeu o valor de um de ponteiro alocado com malloc(), que
tem o tamanho suficiente para acomodar todos os caracteres digitados
antes da marca de fim de linha. ESSE PONTEIRO DEVE SER POSTERIORMENTE
LIBERADO ATRAVÉS DE UMA CHAMADA ``free(line_ptr)´´.
*/

/*
Caso interesse, o if abaixo permite saber se o final da leitura se deu
pela presença da marca de fim de linha ou outra causa (que pode ser
fim de arquivo ou erro -- muitos utilitários do UNIX, incluindo wc, grep,
awk e o próprio compilador C, não consideram que a última linha do arquivo
é uma linha completa se faltar o "\n" nessa linha, mesmo sendo a última;
por exemplo, "echo -n OI | wc -l" produz o resultado "0").
*/
if(b>a){
/* Fim de linha foi encontrado. */
}
else{
/*
Fim de linha não encontrado. Toma as providências cabíveis, talvez
usando feof() e ferror().
*/
}

/*
Usa line_ptr.
*/

free(line_ptr); /* <--- MUITO IMPORTANTE!! */
}
else if(n==0){
/*
O usuário digitou uma linha vazia (i.e., teclou <ENTER> sem digitar
coisa alguma.

O ponteiro line_ptr não foi alterado, e portanto não se deve chamar
``free(line_ptr)´´.
*/
}
else if(n==EOF){
/*
Fim de arquivo ou erro de leitura antes mesmo de ler qualquer caráter.
A variável gloval errno (de <errno.h>) contém a causa do erro.

O ponteiro line_ptr não foi alterado, e portanto não se deve chamar
``free(line_ptr)´´.
*/
}


É óbvio o exemplo acima está expandido. Se não lhe interessar testar e tratar todos os possíveis cenários de execução, você pode omitir as partes que não interessarem, tanto do código como da string de formatação de scanf(). Só não recomendo deixar de testar o valor de retorno, pois é crítico saber se você poderá/deverá chamar free() sobre o ponteiro que poderá ter sido alocado ou não.

O problema com essa abordagem é que ela só funciona na glibc (basicamente no Linux), e somente em versões mais recentes (>= 2.7). Versões um pouco mais antigas da glibc usavam "%as" ou "%a[...]" em lugar de "%ms" e "%m[...]", mas isso foi modificado por causa de confusão com a conversão "%a", especificada pelo padrão ISO C de 1999, que a emprega para ler números de ponto flutuante hexadecimais. Em outros sistemas, é improvável que essa funcionalidade esteja disponível sob qualquer roupagem.

Se você quiser implementar uma coisa nova, minha sugestão é que não toque na implementação da função padronizada fgets(), mas que, ao invés disso, a utilize como peça na construção da sua nova implementação.

3. Re: Função fgets [RESOLVIDO]

Clodoaldo Peres
clodoaldoPeres

(usa Ubuntu)

Enviado em 16/09/2013 - 12:41h

ianclever escreveu:

gente é o seguinte, eu já estou de saco cheio de ter que criar uma variável(vetor) string, para depois ter que ler com fgets, ou getchar, ou gets, ou qualquer outra coisa assim, então quero dar uma melhorada nessa função e faz&#7869;-la alocar isso dinâmicamente em tempo de execução, mas para não precisar reinventar a roda eu quero melhorar da parte que já existe, eu quero saber se alguém tem o código completo da função fgets, já que na biblioteca stdio só tem o protótipo, tá la assim:

 extern char *fgets (char *__restrict __s, int __n, FILE *__restrict __stream); 


onde eu acho a função completa? alguma dica?


Bom,não sei o que exatamente deseja fazer, jah q podes usar malloc para alocar a tua matriz de char, mas segue aí os links dah uma olhada:
http://www.opensource.apple.com/source/Libc/Libc-166/stdio.subproj/fgets.c
e
https://www.planet-source-code.com/vb/scripts/ShowCode.asp?txtCodeId=7425&lngWId=3

OBS: não dou garantia da funcionalidade desses códigos,vc deve testar.
Espero ter ajudado.


4. Re: Função fgets [RESOLVIDO]

ian cléver sales fernandes
ianclever

(usa Arch Linux)

Enviado em 16/09/2013 - 17:27h

obrigado, vou olhar depois, agora estou levando uma surra da lista circular aqui, depois vou quebrar mais a cabeça um pouco.


5. Re: Função fgets [RESOLVIDO]

Paulo
paulo1205

(usa Ubuntu)

Enviado em 19/09/2013 - 17:13h

Sistemas POSIX [POSIX.1-2008] possuem uma função chamada getline() (que é, aliás, um caso particular de getdelim()). Só que POSIX não inclui Windows, MS-DOS e outros sistemas que aparecem por aqui de vez em quando, nem versões mais antigas de sistemas do mundo UNIX.

paulo1205 escreveu:

Se você quiser implementar uma coisa nova, minha sugestão é que não toque na implementação da função padronizada fgets(), mas que, ao invés disso, a utilize como peça na construção da sua nova implementação.


Só para dar uma ideia, eis uma implementação que fiz de uma função que lê uma linha arbitrariamente longa, que funciona com qualquer compilador e em qualquer sistema que implemente a biblioteca padrão. Note como a função usa fgets() para ajudar a construir a função que faz leitura sem limites que tenham de ser definidos a priori.

#include <errno.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int my_fgets(char **str_ptr, FILE *fp){
static const size_t CHUNK_SIZE=512;

int ch;
int str_size, str_length;
char *new_ptr;

if(fp==NULL || str_ptr==NULL){
errno=EINVAL;
return -1;
}

*str_ptr=NULL;

if((ch=fgetc(fp))==EOF)
return 0;

ungetc(ch, fp);

str_length=str_size=0;
while(1){
new_ptr=realloc(*str_ptr, str_size+CHUNK_SIZE);
if(new_ptr==NULL)
return str_size>0? str_length: -1;
*str_ptr=new_ptr;
str_size+=CHUNK_SIZE;

while(str_size-str_length>1){
if(fgets(*str_ptr+str_length, str_size-str_length, fp)==NULL)
return str_length;
str_length+=strlen(*str_ptr+str_length);

if((*str_ptr)[str_length-1]=='\n')
return str_length;
}
}
}


Um exemplo de uso segue abaixo.

int main(void){
char *my_str;
int result;

result=my_fgets(&my_str, stdin); /* <-- Note o "&" antes de my_str */
if(result<0)
printf("Leitura falhou com codigo de erro %d.\n", errno);
else if(result==0){
printf("Zero bytes lidos: %s.\n", feof(stdin)? "fim de arquivo": ferror(stdin)? "erro de operacao": "causa desconhecida");
else{
printf("%d caracteres lidos.\n", result);
printf("%zd caracteres abaixo:\n\t\"%s\"\n", strlen(my_str), my_str);
}

if(result>0)
free(my_str);

return 0;
}



6. Re: Função fgets [RESOLVIDO]

ian cléver sales fernandes
ianclever

(usa Arch Linux)

Enviado em 20/09/2013 - 10:02h

Desculpe a demora, tava meio enrrolado, bem, eu não sabia dessa funcionalidade do scanf, o que eu queria fazer era justamente o que o scanf faz com a opção %m, então, se ja está pronta não há necessidade de reinventar a roda, obrigado a todos que ajudaram.


7. Re: Função fgets [RESOLVIDO]

ian cléver sales fernandes
ianclever

(usa Arch Linux)

Enviado em 20/09/2013 - 10:49h

vc sabem onde eu posso achar todos os possiveis usos do scanf os %algumacoisa dele?


8. Re: Função fgets [RESOLVIDO]

Clodoaldo Peres
clodoaldoPeres

(usa Ubuntu)

Enviado em 20/09/2013 - 10:56h

Segue alguns links:
http://pubs.opengroup.org/onlinepubs/009695399/functions/scanf.html
http://www.cplusplus.com/reference/cstdio/scanf/
Vc pode dar um 'man scanf' no terminal também



9. Re: Função fgets [RESOLVIDO]

ian cléver sales fernandes
ianclever

(usa Arch Linux)

Enviado em 20/09/2013 - 14:13h

clodoaldoPeres escreveu:

Segue alguns links:
http://pubs.opengroup.org/onlinepubs/009695399/functions/scanf.html
http://www.cplusplus.com/reference/cstdio/scanf/
Vc pode dar um 'man scanf' no terminal também


valeu, muito obrigado.


10. Re: Função fgets [RESOLVIDO]

Paulo
paulo1205

(usa Ubuntu)

Enviado em 20/09/2013 - 17:04h

ianclever escreveu:

vc sabem onde eu posso achar todos os possiveis usos do scanf os %algumacoisa dele?


Na documentação on-line do Linux, por exemplo. Digite "man 3 scanf" no seu terminal.

MAS ATENÇÃO: A dica de usar "%ms" ou "%m[...]", embora tenha sido dada por mim, NÃO É o que eu consideraria a melhor resposta. Esse prefixo "m" só funciona em ALGUMAS versões da GLibC (as mais recentes), o que implica, na prática, que também só é encontrada no Linux. Não se deve esperar que funcione em qualquer outro sistema, nem mesmo em qualquer Linux (além de existirem versões mais antigas da GLibC, existem implementações de LibCs alternativas, que enfatizam menor consumo de memória).

Como não se conhece o dia de amanhã, eu recomendo que você procure se desviar o mínimo possível dos padrões existentes. O mais genéricos dos padrões, no contexto de programação em C, é o próprio padrão da linguagem e de sua biblioteca, que é implementado por qualquer ambiente de compilação e execução que diga suportar a linguagem C. Existem também padrões mais específicos, como o POSIX, que estende/altera o padrão da biblioteca do C com funcionalidades específicas (o mundo UNIX geralmente busca estar aderente ao POSIX). Existem ainda padrões e convenções de um SO específico, e existem, por fim, funcionalidades que são específicas de uma implementação e versão em particular.

Qual dos níveis de compatibilidade acima você deve usar num programa seu? Você espera que seu programa continue funcionando mesmo depois que você atualize o sistema? Espera que ele funcione (no caso do Linux) ao executar numa distribuição diferente? Espera que ele funcione no Solaris, ou FreeBSD, ou MacOS (que são POSIX)? Espera que ele funcione (ou que você possa trocar ideias a respeito do seu código com outros programadores) tanto no mundo UNIX quanto no mundo Windows?

(A coisa é um pouco mais complicada porque os padrões também costumam ter versões. O padrão do C, por exemplo, tem três versões principais -- lançadas em 1989 pelo ANSI (ratificada em 1990 pela ISO), em 1999 pela ISO, e em 2011 pela ISO -- e algumas revisões intermediárias. Os compiladores C da Microsoft intencionalmente se limitam a implementar somente a versão de 1989, e isso também deverá entrar na sua equação ao decidir que nível de funcionalidade vai poder usar.)

No mundo POSIX existe uma função chamada getline() que faz o que você quer e não é específica somente do binômio Linux com GLibC, mas não vai funcionar no Windows. A implementação de my_fgets(), que eu mostrei acima, funciona em qualquer compilador compatível com o padrão C de 1989 em diante.


11. Re: Função fgets [RESOLVIDO]

ian cléver sales fernandes
ianclever

(usa Arch Linux)

Enviado em 21/09/2013 - 00:44h

ok, estou iniciando ainda no c, ainda tenho muito que aprender, mas devagarsinho eu vou chegando la, vou prestar mais atenção a essas, coisas, o linux é o sistema que eu uso e defendo, mas quem programa tem que ser multiplataforma, infelismente, nem todo mundo gosta de linux. obrigado pela dica.


12. Re: Função fgets [RESOLVIDO]

ian cléver sales fernandes
ianclever

(usa Arch Linux)

Enviado em 21/09/2013 - 00:46h

legal, eu não sabia disso as man's do linux também tem documentação de funções bom saber disso.



01 02 03



Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts