Função fgets [RESOLVIDO]

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

ian cléver sales fernandes
ianclever

(usa Arch Linux)

Enviado em 27/10/2013 - 02:12h

bem, agora que estou começando a ver manipulação de arquivos, comecei a entender melhor o código do colega, mas algumas partes eu ainda estou boiando, em fim, agora eu consegui criar uma função que consegue fazer o que eu queria( bem simples, mas eficaz), se alguem quizer fazer melhorias aceito sugestões, segue o código:


#include <stdio.h>
#include <stdlib.h>

char *dinamica(char *aloca)
{
char a;
int c=1,c2=1;
a=getchar();
aloca=(char *)malloc(sizeof(char));
aloca[0]=a;
while(a!='\n')
{
aloca=(char *)realloc(aloca,++c*sizeof(char));
a=fgetc(stdin);
aloca[c2++]=a;
}
__fpurge(stdin);
aloca=(char *)realloc(aloca,++c*sizeof(char));
aloca[c2]='\ 0';// é barra zero junto, ta com espaço pq tava dando bug na visualização no site.
return aloca;
}
void main()
{
char *p;
p=dinamica(p);
printf("\n%s",p);
free(p);
}




  


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

Paulo
paulo1205

(usa Ubuntu)

Enviado em 28/10/2013 - 12:27h

Já que você abriu a comentários, tenho um bocado deles a fazer.

1) Declarar o tipo de retorno de main() como void é errado. O padrão do C dá duas opções de declaração para main() em implementações auto-hospedadas (i.e. que executem sobre um sistema operacional como Linux ou Windows):

/* quando o S.O. não passa argumentos ao programa */
int main(void)


ou

/* quando o programa deseja receber argumentos do S.O. */
int main(int argc, char *argv[]) /* pode ser "char **argv", que é um sinônimo */
.

2) Código com fpurge(), __fpurge() ou fflush() aplicado a stream de leitura deve sempre ser olhado com desconfiança. Simplesmente não vejo qualquer razão para que você use alguma dessas funções: lendo caráter a caráter, como você está fazendo, não existe motivo algum para você ter problema com "lixo" no buffer.

3) O tipo de retorno de fgetc() é int. Declarar a (a variável que recebe o valor retornado pela função) como char impede seu programa de detectar devidamente a sinalização de erros que possam ocorrer durante a execução da função.

4) Ainda neste respeito, você deveria incluir no seu laço de repetição não apenas a espera pela marca de fim de linha, mas também a detecção de erro, quando fgetc() retorna a constante EOF (que é um inteiro, e não cabe num char).

5) Pode haver erros em outras partes da função, também. Já lhe ocorreu a possibilidade de que malloc() e realloc() podem falhar?

6) Sua forma de chamar realloc() incorre num erro bastante comum, que é o de usar o próprio ponteiro que se está tentando realocar como destino da atribuição do valor da função. O problema de fazer isso é que, se a realocação falhar, o valor do ponteiro original poderá ser sobrescrito com um ponteiro nulo, mas a área originalmente alocada continuará alocada. Como o endereço que apontava para essa área foi sobrescrito com o valor do ponteiro nulo, seu programa não vai mais conseguir ter acesso a ela nem desalocá-la.

7) Em caso de erro, seria bom sua função sinalizar que não conseguiu fazer a leitura. Uma sugestão que eu dou, e que se aproxima do comportamento normalmente encontrado em funções da biblioteca padrão, é que a função retorne um ponteiro nulo se ocorrer algum erro.

8) Uma vez que a função poderá devolver um ponteiro nulo, você deverá tomar cuidado com as operações que fizer com o valor retornado por ela (por exemplo: você sabe como printf() se comporta quando você manda imprimir uma string, mas o ponteiro que deveria apontar para tal string é, na verdade, um ponteiro nulo?).

9) Não que seja um erro, mas é muito ineficiente alocar e realocar a string para cada caráter lido. Normalmente você escolhe um tamanho inicial presumido para o caso mais comum de texto esperado mais uma pequena folga (algo como 50 ou 80 carcateres são valores comuns), e vai aumentando o tamanho da string também em blocos do mesmo tamanho (ou multiplicando o tamanho corrente por um fator, como para aumentar 25% ou 50% a cada vez que exceder o tamanho previsto).

10) Você certamente conseguiria eliminar algumas dessas variáveis. Note, por exemplo, que c e c2 praticamente "andam juntas", e isso sugere que pelo menos uma delas não é tão necessária quanto numa primeira análise você possa ter imaginado.

Se tiver qualquer dúvida, pode perguntar.


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

ian cléver sales fernandes
ianclever

(usa Arch Linux)

Enviado em 28/10/2013 - 16:03h

ali eu coloquei void main pq era apenas um teste, ela ainda estava meio que artesanal,mas sanando alguns dos erros que vc falou, hoje ela está assim:


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

char *dinamica(char *aloca)
{
char a,*aux;
int c=1;
a=getchar();
aloca=(char *)malloc(sizeof(char));
if(aloca==NULL)
{
puts("\nFalha na alocação de memória!");
exit(0);
}
else
{
aloca[0]=a;
}
while(a!='\n')
{
a=(char)fgetc(stdin);
if(a!='\n')
{
aux=(char *)realloc(aloca,++c*sizeof(char));
if(aux==NULL)
{
puts("\nFalha na alocação de memória!");
exit(0);
}
else
{
aux[c-1]=a;
aloca=aux;
}
}
}
__fpurge(stdin);
aux=(char *)realloc(aloca,++c*sizeof(char));
if(aux==NULL)
{
puts("\nFalha na alocação de memória!");
exit(0);
}
else
{
aux[c-1]='\ 0';
aloca=aux;
}
return aloca;
}
int main(void)
{
char *p;
p=dinamica(p);
printf("\n%s",p);
free(p);
return 0;
}


agora a parte de limpar o buffer é necessária, da maneira que eu tinha postado estava pegando o \n, mas minha intenção não era essa, depois que eu vi esse bug e corrigi, por isso acaba tendo que limpar o buffer, se eu fosse ler o \n ai eu ia colocar o delimitador como EOF, e eu até tentei, mas mesmo com uma variável inteira ele não estava achando o EOF, que suponho que seja a marca de fim de arquivo(me corrija se estiver errado),
aí me surgiu uma dúvida o stdin não tem uma marca de fim de arquivo, um EOF como os outros arquivos comuns?


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

Paulo
paulo1205

(usa Ubuntu)

Enviado em 29/10/2013 - 05:40h

Eu passei por cima de alguns outros problemas no código anterior, que sobreviveram também no de agora.

11) Você declara aloca como parâmetro da função dinamica(), mas não a usa realmente como parâmetro, pois age sobre ela de modo que em nada depende do valor com o qual a função foi chamada. Já que aloca tem o comportamento de variável interna à função, seria melhor declará-la como variável interna, deixando a função sem qualquer argumento formal. A declaração da função ficaria

char *dinamica(void) 


e seu uso ficaria mais oou menos como segue.

char *p;
p=dinamica();


12) O nome da função dinamica() é ruim, pois não descreve adequadamente o que a função faz. Entendo que o propósito primordial dela é ler uma linha tão longa quanto se queira; que para isso se use alocação dinâmica de memória é um aspecto secundário (embora necessário e necessitante da atenção do programador, dada a natureza da linguagem C). O nome deveria refletir primeiramente a função primordial, e os detalhes de implementação, caso tenham de aparecer, ficariam como um prefixo ou sufixo. Se você aceitar sugestões, considere algo como readline(), ou areadline() (onde esse primeiro 'a' é um prefixo que indica alocação dinâmica, como em asprintf()).

13) Por que amarrar sua função à leitura de stdin? Você poderia fazer uma função que lesse de um stream qualquer, passado como parâmetro à função, de modo a poder reutilizá-la mais facilmente quando começar a trabalhar com arquivos.

----

Já no programa novo, seguem meus comentários (alguns deles reedições de comentários feitos no programa anterior).

1) Você continua usando uma variável do tipo char para recebr o valor retornado por fgetc(), o que é errado porque o tipo de valor devolvido por essa função não é char, mas int.

2) Você continua usando a famigerada __fpurge(). Eu GARANTO que você não precisa dela, até porque você somente a chama depois de já ter saído do laço de repetição, o que significa que o '\n' já foi consumido pela chamada a fgetc(). A presença desse __fpurge(), portanto, o faz desprezar uma segunda linha. Duvido que seja isso mesmo o que você quer.

3) Você continua não tratando devidamente eventual fim de arquivo ou erro de leitura no seu laço de repetição. Se o fim de arquivo ocorrer antes de você encontrar uma linha completa (i.e. o caráter '\n'), seu programa vai entrar em loop infinito, com fgetc() sempre devolvendo o valor EOF, que você não trata.

4) Abortar o programa em caso de falha na alocação de memória me parece muito radical. Mais radical ainda no caso de realocação, principalmente se você considerar que uma parte da linha já pode ter sido lida com sucesso. Minha sugestão é que você não tome atitudes drásticas dentro de funções genéricas, mas que seja generoso com o sucesso parcial e sinalize eventuais erros a quem chamou a função, para que o chamador decida como agir.

5) É claro que, para que o sucesso parcial possa ser realmente útil, você não deve provocar perda de dados lidos nem retornar uma string na qual falte o byte nulo que deveria indicar onde ela termina. Por isso, em lugar de deixar a alocação de espaço e o preenchimento do byte nulo somente para o final da função, você deveria se preocupar com isso desde o início da função e a cada iteração do seu laço de repetição.

6) Se for para abortar a execução, não use exit(0), pois tradicionalmente o 0 indica execução bem-sucedida (i.e. com zero erros). E se você quiser imprimir uma mensagem de erro antes de abortar, envie-a para stderr, não para stdout. Considere usar a função perror(), declarada em <errno.h>.

7) Você repete em diferentes partes do corpo da função os mesmos blocos de código ou blocos com funções parecidas. Se você rearrumar seu laço de repetição, provavelmente vai conseguir reduzir os três blocos que cuidam de alocação de memória para dois ou mesmo para apenas um. Não tenha medo de usar, se necessário, break, continue ou return dentro de um bloco if ou else (e se algum zé-mané vier reclamar de que isso quebra a programação estruturada, ignore-o e mande-o ir aprender a programar antes de falar asneiras -- a não ser que seja o seu professor, caso em que você faz do jeito que ele mandar, para garantir sua nota, e depois de passar na disciplina, ignora as bobagens que ele tiver dito e passa a fazer do modo certo).

ianclever escreveu:

agora a parte de limpar o buffer é necessária, da maneira que eu tinha postado estava pegando o \n, mas minha intenção não era essa, depois que eu vi esse bug e corrigi, por isso acaba tendo que limpar o buffer, se eu fosse ler o \n ai eu ia colocar o delimitador como EOF, e eu até tentei, mas mesmo com uma variável inteira ele não estava achando o EOF, que suponho que seja a marca de fim de arquivo(me corrija se estiver errado),
aí me surgiu uma dúvida o stdin não tem uma marca de fim de arquivo, um EOF como os outros arquivos comuns?


Reter o '\n' na string lida às vezes parece um despropósito, mas é importante para saber se houve uma interrupção prematura do fluxo de dados. Numa função de leitura genérica, eu recomendaria deixar o '\n' lido na string, e ter uma outra função para extirpá-lo de onde ele não for desejado (mas a critério de quem chamar as funções e não arbitrariamente, dentro da função de leitura). Uma função de extirpação é tão fácil de fazer que pode até ser feita com uma macro.

#define chop_nl(str) { int last=strlen(str)-1; if(last>=0 && str[last]=='\n') str[last]=0; } 


(Não que eu recomende o uso como macro -- só coloquei para mostrar como é simples. Uma função é melhor, especialmente se estiver trabalhando com uma versão do C que aceite funções inline.)

Vou lhe mostrar uma implementação da sua função com um laço de repetição melhorado. Não é a forma que eu usaria porque ainda acho muito ineficiente realocar a memória para cada caráter lido (a froma que eu usaria eu já havia mostrado uma duas ou três postagens atrás, na função my_fgets()). Mesmo assim, note como é um código enxuto, especialmente se você desconsiderar os comentários.

char *areadline(FILE *stream){
char *string, *new_string;
size_t string_size;
int ch;

string=NULL;
string_size=1; /* Pressupõe espaço para o byte nulo, mesmo se string estiver vazia. */
do {
ch=fgetc(stream);
if(ch==EOF)
return string; /* Pode ser nulo, indicado falha total, ou pode retornar leitura parcial. */

/* Não precisa multiplicar por sizeof(char) pois sizeof(char)==1 por definição. */
/* Quando string==NULL, realloc() é sinônimo de malloc(), também por definição. */
new_string=realloc(string, string_size+1);
if(new_string==NULL){
ungetc(ch, stream); /* Devolve byte lido ao buffer, para não perder dados. */
return string; /* Pode ser nulo, indicado falha total, ou pode retornar leitura parcial. */
}

string=new_string;
string_size++;
string[string_size-2]=ch;
string[string_size-1]=0;
} while(ch!='\n');
/*
Não recomendo remover o '\n', nem muito menos se preocupar com encolher
em um miserável byte o tamanho da string se você decidir remover tal
caráter. Mas se você realmente quiser insistir com isso, este aqui seria
o ponto em que você poderia chamar chop_ln (ver acima) e realloc().
*/
return string;
}



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

ian cléver sales fernandes
ianclever

(usa Arch Linux)

Enviado em 29/10/2013 - 12:41h

agora sim enxuguei ela bastante, ela só está maiorsinha assim pq eu coloquei para ficar a critério do usuário se ele quer \n ou não, agora quanto a limitar ela a stdin por enquanto é só isso mesmo que eu quero mesmo, é só para ela fazer o mesmo papel do parâmetro %m do scanf, resolvi padronizar isso, pq eu e os colegas meus que usam GNU/Linux, já tem o privilégio de tem o parametro %m nativo no scanf, mas a maioria da sala usa o Windows e as vezes a gente perde muito tempo da aula por conta dessas alocações dinâmicas de strings, aí eu resolvi dar essa colher de chá, e também para facilitar minha vida, já pensou se o pessoal tira o parâmetro %m do scanf? aí eu ia ficar na mão também.
o código ficou assim:

char *agets(char a)
{
int ch,tam=2;
char *str=NULL;
ch=getchar();
while(ch!='\n' && ch!=EOF)
{
if(tam>2)
{
ch=fgetc(stdin);
}
if(a=='\n')
{
if(ch!='\n')
{
str=(char *)realloc(str,tam*sizeof(char));
if(str==NULL)
{
return str;
}
else
{
str[tam-2]=ch;
str[tam-1]='{TTEXTO}';
tam++;
}
}
}
else
{
if(a==' ')
{
str=(char *)realloc(str,tam*sizeof(char));
if(str==NULL)
{
return str;
}
else
{
str[tam-2]=ch;
str[tam-1]='\ 0';
tam++;
}
}
else
{
break;
}
}
}
return str;
}


uso dela:

int main(void)
{
char *p;
p=agets('\n');
printf("\n%s",p);
free(p);
return 0;
}


se eu passar !( é para coincidir com o negador do c mesmo para facilitar) ele vai ignorar os \n ' s que ele encontrar pelo caminho. e se eu passar \n como parâmetro ele vai utilizar esse ou esses \n que ele encontrar no caminho.




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

ian cléver sales fernandes
ianclever

(usa Arch Linux)

Enviado em 29/10/2013 - 12:43h

e é claro, muito obrigado pelas dicas.
e aproveitando, para eu adicionar essa função a biblioteca padrão dá trabalho? ela pode ser passada do jeito que está ou tem que ser adaptada?


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

Paulo
paulo1205

(usa Ubuntu)

Enviado em 29/10/2013 - 19:05h

Vejo que você voltou a incorrer num erro que já tinha corrigido, usando a mesma variável como argumento de realloc() e para receber o valor de retorno. Já lhe expliquei o motivo de isso ser errado anteriormente. Sugiro que você volte a corrigir isso, pois é um erro crasso.

Em minha última mensagem, depois de sugerir que você simplificasse seu código, eu mostrei a implementação mais simples possível, sem repetições de código desnecessárias, mas com todos os instrumentos possíveis de detecção e informação sobre erros, na esperança de que você lesse e entendesse, ou que tentasse tirar qualquer dúvida que tivesse. É um pouco decepcionante constatar que, mesmo depois disso, você aumentou ainda mais a quantidade de código duplicado.

O laço de repetição com while é normalmente usado para controlar algo que pode ser executado zero ou mais vezes, ao passo que aquele com do-while é adequado para laços que devem ser executados pelo menos uma vez ou mais. Ao tentar usar a estrutura de "zero ou mais" para controlar algo que tem de ser executado pelo menos uma vez, você teve de inventar uma variável de controle a mais (tam) e introduzir uma execução a mais do comando de leitura antes de iniciar o laço de repetição (e, o que é pior ainda, você o fez de modo inconsistente, pois num lugar você usou getchar() e noutro, fgetc()). Resultado: código mais longo, mais propenso a erro, mais difícil de entender.

Na hora de implementar um conjunto de funcionalidades parecidas, mas com ligeiras variações entre si, é preferível ter funções separadas para cada uma delas a ter uma só função com um parâmetro de controle a mais para indicar o modo como essa função única deve se comportar. Isso é particularmente verdadeiro quando a quantidade de variações é pequena e quando existe uma função básica que pode ser aproveitada na execução das demais. Desse modo, se você quer poder escolher uma função de leitura que mantém o '\n' e outra que o extirpa, eu sugiro que você tenha duas funções separadas e que evite a duplicação de código, tentando aproveitar a mais genérica na implementação da mais específica. Assim sendo, usando código que eu postei anteriormente de areadline() e chop_nl(), é possível criar uma função que lê uma linha e extirpa o '\n' fazendo tão somente o seguinte.

inline char *areadline_nonl(FILE *stream){
char *str;
str=areadline(stream);
if(str!=NULL)
chop_nl(str);
return str;
}


----

Com relação a sua pergunta sobre alterar a biblioteca padrão, a primeira coisa a dizer é que você deve ter cuidado com as palavras. A biblioteca padrão é um padrão internacional. Para, literalmente, adicionar uma nova função à biblioteca padrão, o primeiro passo seria integrar o grupo de trabalho certo de uma das entidades internacionais que se reúnem em comitê para definir um padrão para linguagem C e para sua biblioteca, que depois será homologado pela ISO (Organização Internacionais para Padronizações).

Possivelmente você não quis dizer o que disse, mas sim embutir sua função na biblioteca C da sua máquina. É possível, se você obtiver o código fonte da LibC (possivelmente a GLibC, que é a que a maioria das distribuições de Linux usa), colocar nele suas alterações, recompilá-lo e instalar em sua máquina a nova biblioteca.

Talvez esse tipo de coisa seja até um exercício interessante para um dia, num futuro não muito imediato. Você ainda tem de adquirir muita bagagem de programação antes de ter de se preocupar com isso.

Além do mais, ter uma LibC customizada, diferente do resto do universo, é pedir para ter uma dor de cabeça permanente com atualizações. Está pronto para o trabalho de integrar código fonte alheio às modificações que fizer na sua própria máquina?

Acho que o melhor mesmo é você fazer a sua própria biblioteca, separada das que acompanham o sistema. Isso facilitará sua vida quando quiser levar suas funções para outra máquina ou até mesmo para outro SO.

Em todo caso, as funções discutidas neste tópico são totalmente redundantes. Sistemas POSIX têm a função getline(), que faz todo o que foi discutido aqui sobre leitura arbitrariamente longa com alocação de memória, e de uma maneira mais eficiente do que tudo o que até agora você mostrou ou que eu mostrei a você.


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

ian cléver sales fernandes
ianclever

(usa Arch Linux)

Enviado em 30/10/2013 - 01:46h

eu li seus comentários, e sim no caso o certo mesmo seria usar o do while, não sei se vc percebeu, para vc q já tem mais experiência pode até parecer bobagem, mas, eu estou usando o while, pq eu não sei fazer uma forma mais compacta que o getchar para abrir o stdin para escrita(e sem dar bug), eu até pensei de abrir ele para leitura ou reabrir com freopen, só que eu não sei como retornar ele ao estado que ele estava antes de eu alterar, por isso eu usei o getchar para abrir o stdin para escrita, literalmente sujar o buffer, pegar o primeiro caractere, e depois o resto(se houver) com fgetc,pq o fgetc já pré supõe que eu tenha algo no stdin, se não tiver ele não vai ler nada(me corrija se eu estiver errado), e o que eu quero é abrir o stdin para eu escrever diretamente até que eu pressione enter e depois ler o que ficou lá.
se eu colocasse o getchar dentro do while ou do while ou for, ele vai abrir o stdin para eu digitar todas as vezes que passar no laço e não é isso que eu quero.

agora quanto a duplicação na verdade mesmo eu particularmente prefiro o \n longe da string em si, so coloquei ali pq vc mencionou o caso de alguém querer usa-lo, eu prefiro formatar do jeito que eu quero por fora mesmo, posso estar enganado, mas no meu caso caso(que não quero o \n) apesar de ser uma real perda de dados ignorar o \n, não seria menos trabalhoso(computacionalmente falando) já ignorá-lo direto no laço ao invés de adicioná-lo a string, alocar espaço para ele, depois criar outro laço procurar por ele remover, e realocar o espaço?

se eu for lendo do stdin direto com fgetc, fgets, etc. sem adicionar nada a ele não vai ler algo e vai me retornar ou EOF ou outra coisa do tipo?
pois é no caso quando eu postei a pergunta sobre o fgets, eu não sabia que ele era usado para ler arquivos e que fgets(string,tamanho,stdin) na verdade esta lendo de um stream chamado stdin(que no meu caso é o teclado), aí vc me falou do parâmetro %m do scanf, que fazia exatamente o que eu queria, a função que eu quero implementar não é uma função complexa para ler de diversas streams ou arquivos, eu quero somente ler uma string dinâmicamente( digitar uma string do tamanho que eu quiser sem passar o tamanho préviamente e ao final isso vai me retornar um vetor de char funcional(com \ 0 e tudo) , e que funcione tanto em GNU/Linux quanto em Windows, etc. ou seja a mesma coisa que o scanf("%m",&variavel); faria, com isso deixaria de ser uma particularidade só do GNU/Linux, pq como eu já disse até então não se tem( pelo menos eu não conheço)uma função pronta que faça isso no Windows, e muitos colegas meus la da faculdade usam esse maldito sistema, e esse problema já tem tomado muito tempo das aulas com uma coisa simples dessas, por isso eu resolvi acabar com esse problema, quando tiver 100% a função eu vou passar para todos lá da sala para acabar com esse problema e podermos nos preocupar com o que realmente interessa, por isso eu queria adicioná-lo a uma biblioteca, não precisa ser a padrão(obrigado pela correção eu falei besteira mesmo), mas uma biblioteca que seja reconhecida quando eu der um #include <biblioteca.h>
eu nunca criei uma biblioteca então não tenho noção de como mandar essa função para uma, e nem onde colocar essa biblioteca(vou dar uma pesquisada, mas aceito sugestões).


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

Paulo
paulo1205

(usa Ubuntu)

Enviado em 31/10/2013 - 16:15h

ianclever escreveu:

eu li seus comentários, e sim no caso o certo mesmo seria usar o do while, não sei se vc percebeu, para vc q já tem mais experiência pode até parecer bobagem,


Eu sei que você é iniciante, sim. Não o recrimino, nem a qualquer outra pessoa que venha aqui, por ser iniciante. Penso que o tempo gasto e a quantidade e qualidade de dicas que estou dando a você ao longo da discussão deveria ser um indicativo claro justamente de estímulo, não de recriminação.

Aliás, é justamente por você ser um iniciante que, entre minhas dicas, incluo várias recomendações para que você se afaste desde cedo de vícios de programação que podem lhe causar problemas mais a frente, principalmente na medida em que o tamanho dos seus programas crescer (sem exagero, se você seguir carreira na área, logo, logo seus programas terão alguns, ou mesmo muitos, milhares de linhas).

Como programador ou analista, você tem de saber compreender o problema como um todo e usar a técnica de ir dividindo, em etapas sucessivas, o problema em partes cada vez menores, identificando eliminando/agrupando possíveis redundâncias ao longo do processo, até chegar ao nível de instruções diretamente mapeáveis num programa. Só que compete também a você conhecer as ferramentas de trabalho a sua disposição (i.e. as linguagens de programação, técnicas de codificação, padrões de projeto, ambientes de desenvolvimento, sistemas operacionais etc.), justamente para que o mapeamento de instruções seja feito da forma mais simples e eficiente possível.

mas, eu estou usando o while, pq eu não sei fazer uma forma mais compacta que o getchar para abrir o stdin para escrita(e sem dar bug), eu até pensei de abrir ele para leitura ou reabrir com freopen, só que eu não sei como retornar ele ao estado que ele estava antes de eu alterar, por isso eu usei o getchar para abrir o stdin para escrita, literalmente sujar o buffer, pegar o primeiro caractere, e depois o resto(se houver) com fgetc,pq o fgetc já pré supõe que eu tenha algo no stdin, se não tiver ele não vai ler nada(me corrija se eu estiver errado), e o que eu quero é abrir o stdin para eu escrever diretamente até que eu pressione enter e depois ler o que ficou lá.
se eu colocasse o getchar dentro do while ou do while ou for, ele vai abrir o stdin para eu digitar todas as vezes que passar no laço e não é isso que eu quero.


Como assim "abrir o stdin para escrita"? Nem getchar() nem fgetc() "abrem" coisa alguma, mas ambas consomem dados de um stream de leitura previamente aberto.

stdin é um stream de leitura global (uma variável global declarada em <stdio.h> e implementada pela biblioteca padrão) que o ambiente de execução de um programa em C abre automaticamente, e apenas uma vez, cada vez que o programa é executado, antes mesmo de se iniciar a execução de main(). Por sua vez, getchar() é um sinônimo de fgetc(stdin).

Assim sendo, nada do que você disse no parágrafo anterior faz muito sentido.

Que material você está usando para aprender C? Talvez você tenha de trocar para algo melhor.

agora quanto a duplicação na verdade mesmo eu particularmente prefiro o \n longe da string em si, so coloquei ali pq vc mencionou o caso de alguém querer usa-lo, eu prefiro formatar do jeito que eu quero por fora mesmo, posso estar enganado, mas no meu caso caso(que não quero o \n) apesar de ser uma real perda de dados ignorar o \n, não seria menos trabalhoso(computacionalmente falando) já ignorá-lo direto no laço ao invés de adicioná-lo a string, alocar espaço para ele, depois criar outro laço procurar por ele remover, e realocar o espaço?


Como eu disse anteriormente, ter a marca de fim de linha é importante para saber se houve truncamento ou interrupção prematura do fluxo de leitura.

Imagine uma aplicação que leia um arquivo linha a linha e grave as linhas lidas num outro arquivo. Ela poderia ser grosseiramente implementada de um dos dos modos abaixo.

/* Modo 1: com uma função de leitura que retém o '\n'. */
char *line;
/* Usa o areadline() que eu implementei acima. */
while((line=areadline(input_file))!=NULL){
fputs(line, output_file);
free(line);
}


/* Modo 2: com uma função de leitura que descarta o '\n'. */
char *line;
/* Usa a areadline_nonl() que eu implementei acima. */
while((line=areadline_nonl(input_file))!=NULL){
fprintf(output_file, "%s\n", line);
free(line);
}


Pouca diferença, não é? O segundo código tem de forçar a impressão do '\n' ao final de cada linha. Mas isso implica que, se o arquivo de origem não tiver a marca de fim de linha na última linha do arquivo, o primeiro programa produz um arquivo de saída idêntico ao de entrada, ao passo que o segundo programa não teria como o fazer.

(Eu reconheço que o exemplo é simplório -- mas, por isso mesmo, penso que ele é bastante didático. Você poderia tentar fazer "bruxarias" com feof(), ferror() e a variável global errno para tentar adivinhar se deveria ou não incluir o '\n' no arquivo de saída, mas receio que nunca teria total certeza de estar acertando na adivinhação. E você teria um código certamente muito mais comprido do que apenas 5 linhas (descontando as de comentários).)

Funções de bibliotecas são normalmente pensadas para ser tão úteis quanto possível, minimizando ao máximo a possibilidade produzir resultados errados. Pode parecer chocante a princípio, mas reter o '\n' dá mais chances de produzir resultados corretos do que a opção de descartá-lo.

Eu já estive no seu lugar. É óbvio que eu não nasci sabendo. Quando eu fazia um programa que tinha de ler textos de arquivos, eu achava "um saco" o fato de fgets() reter o '\n', obrigando-me a extirpá-lo manualmente. Por que ela não era mais parecida com gets()? Eu preferia apelar a fscanf() ou reimplementar, como você mesmo está fazendo ao longo desta discussão, uma "gets() só minha para arquivos". Invariavelmente, eu tinha sempre muito mais trabalho reinventando a roda e caçando bugs -- até porque eu não dominava as ferramentas ao meu alcance -- do que teria tido simplesmente escrevendo algo como a chop_nl() que mostrei acima e usando-a nos lugares em que fosse necessária.

Depois eu compreendi -- claro que por "iluminação" recebida de terceiros mais experientes do que eu -- que a própria gets() era uma aberração de segurança e exemplo máximo de péssimo projeto -- tanto que ela foi finalmente removida da última versão do padrão do C, de 2011. Para produzir programas mais seguros, tive de parar de usá-la até mesmo para ler de stdin. E aí mesmo é que eu passei um tempo penando com minhas reinvenções da roda, pois aos bugs de antes somavam-se agora os problemas de "lixo no buffer". Só com o tempo, e depois de muita cabeçada, eu constatei que fgets() teria sido a melhor resposta desde o começo.

se eu for lendo do stdin direto com fgetc, fgets, etc. sem adicionar nada a ele não vai ler algo e vai me retornar ou EOF ou outra coisa do tipo?


Não compreendi o que você tentou dizer. Adicionar o quê a stdin, e como?

pois é no caso quando eu postei a pergunta sobre o fgets, eu não sabia que ele era usado para ler arquivos e que fgets(string,tamanho,stdin) na verdade esta lendo de um stream chamado stdin(que no meu caso é o teclado), aí vc me falou do parâmetro %m do scanf, que fazia exatamente o que eu queria, a função que eu quero implementar não é uma função complexa para ler de diversas streams ou arquivos, eu quero somente ler uma string dinâmicamente( digitar uma string do tamanho que eu quiser sem passar o tamanho préviamente e ao final isso vai me retornar um vetor de char funcional(com \ 0 e tudo) , e que funcione tanto em GNU/Linux quanto em Windows, etc. ou seja a mesma coisa que o scanf("%m",&variavel); faria, com isso deixaria de ser uma particularidade só do GNU/Linux, pq como eu já disse até então não se tem( pelo menos eu não conheço)uma função pronta que faça isso no Windows, e muitos colegas meus la da faculdade usam esse maldito sistema, e esse problema já tem tomado muito tempo das aulas com uma coisa simples dessas, por isso eu resolvi acabar com esse problema, quando tiver 100% a função eu vou passar para todos lá da sala para acabar com esse problema e podermos nos preocupar com o que realmente interessa, por isso eu queria adicioná-lo a uma biblioteca, não precisa ser a padrão(obrigado pela correção eu falei besteira mesmo), mas uma biblioteca que seja reconhecida quando eu der um #include <biblioteca.h>


Eu entendo perfeitamente sua necessidade. Acho que quase todo mundo que trabalhou com strings em C já passou por isso.

Ao longo deste tópico eu mostrei duas soluções prontas para você ler e tentar entender: my_fgets() e areadline(). As duas funcionariam muito bem no Windows, também. Eu não quero sugerir que você as use de pronto, mas que as leia e procure compreendê-la totalmente, pois ambas têm coisas a lhe ensinar.

eu nunca criei uma biblioteca então não tenho noção de como mandar essa função para uma, e nem onde colocar essa biblioteca(vou dar uma pesquisada, mas aceito sugestões).


Uma biblioteca é simplesmente uma coleção de objetos pré-compilados que podem ser agregados ao seu programa. Procure ler sobre compilação em separado (separate compilation) e arquivos objeto (object files).

Existem vários tutoriais sobre criação de bibliotecas na Web. Googlando, achei este aqui (http://www.yolinux.com/TUTORIALS/LibraryArchives-StaticAndDynamic.html), que me pareceu razoável.


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

ian cléver sales fernandes
ianclever

(usa Arch Linux)

Enviado em 03/11/2013 - 23:34h

[code]
---se eu for lendo do stdin direto com fgetc, fgets, etc. sem adicionar nada a ele não vai ler algo e vai me retornar ou EOF ou outra coisa do tipo?-----


Não compreendi o que você tentou dizer. Adicionar o quê a stdin, e como?[code]

não é adicionar, na verdade é ler, agora eu estou vendo que o fgetc() tem a mesma funcionalidade do getchar(), mas agora me surgiu uma duvida:

sendo o stdin uma espécie de arquivo(stream), teóricamente, se eu não tivesse escrito nada previamente no stdin, assim como aconteceria em qualquer outro arquivo em disco, não era para ele retornar o fim de arquivo(EOF) ao invés ficar aguardando eu digitar algo(que seria no caso uma ação de escrita no arquivo)?

era por isso que eu estava usando o getchar, porque eu pensava que o fgetc fazia somente a função de leitura de um arquivo que já tinha algo préviamente escrito, enquanto que o getchar faria uma espécie de "escrita" no stdin e posteriormente leria o primeiro caracter adicionado a ele. Por isso que eu estava de certa forma "escrevendo" a string no stdin lendo o primeiro caractere e deixando o fgetc ler o que sobrou(já que até então eu achava que ele só lia), vc poderia me explicar melhor como o getchar e o fgetc funcionam em relação especificamente ao stream stdin?


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

ian cléver sales fernandes
ianclever

(usa Arch Linux)

Enviado em 03/11/2013 - 23:41h

paulo1205 escreveu:

se eu for lendo do stdin direto com fgetc, fgets, etc. sem adicionar nada a ele não vai ler algo e vai me retornar ou EOF ou outra coisa do tipo?


Não compreendi o que você tentou dizer. Adicionar o quê a stdin, e como?



desculpe deu um bug na visualização acima é isto:

não é adicionar, na verdade é ler, agora eu estou vendo que o fgetc() tem a mesma funcionalidade do getchar(), mas agora me surgiu uma duvida:

sendo o stdin uma espécie de arquivo(stream), teóricamente, se eu não tivesse escrito nada previamente no stdin, assim como aconteceria em qualquer outro arquivo em disco, não era para ele retornar o fim de arquivo(EOF) ao invés ficar aguardando eu digitar algo(que seria no caso uma ação de escrita no arquivo)?

era por isso que eu estava usando o getchar, porque eu pensava que o fgetc fazia somente a função de leitura de um arquivo que já tinha algo préviamente escrito, enquanto que o getchar faria uma espécie de "escrita" no stdin e posteriormente leria o primeiro caracter adicionado a ele. Por isso que eu estava de certa forma "escrevendo" a string no stdin lendo o primeiro caractere e deixando o fgetc ler o que sobrou(já que até então eu achava que ele só lia), vc poderia me explicar melhor como o getchar e o fgetc funcionam em relação especificamente ao stream stdin?


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

Paulo
paulo1205

(usa Ubuntu)

Enviado em 04/11/2013 - 02:04h

ianclever escreveu:

não é adicionar, na verdade é ler, agora eu estou vendo que o fgetc() tem a mesma funcionalidade do getchar(), mas agora me surgiu uma duvida:

sendo o stdin uma espécie de arquivo(stream), teóricamente, se eu não tivesse escrito nada previamente no stdin, assim como aconteceria em qualquer outro arquivo em disco, não era para ele retornar o fim de arquivo(EOF) ao invés ficar aguardando eu digitar algo(que seria no caso uma ação de escrita no arquivo)?


Um stream não é necessariamente um arquivo. Como você deve saber, stream significa literalmente "fluxo", "corrente" (não como cadeia, mas como uma corrente marinha), "córrego". No caso da biblioteca padrão do C, um stream é isso mesmo: uma corrente de dados (na forma de bytes, que em C são sinônimos de caracteres) que o programa pode receber de alguma fonte externa ou enviar para algum destino externo. As correntes de dados que chegam ao programa são ditas "de entrada" ou "de leitura", ao passo que dados gerados pelo programa e encaminhados para outro lugar são postos em correntes "de saída" ou "de escrita".

A "mágica" que permite que a forma de ler de um arquivo em disco seja a mesma da de ler do teclado é uma preocupação que não cabe ao usuário da linguagem C, mas sim a quem implementa o ambiente de programação em C, que tem de atender àquilo que o padrão determina.

Historicamente, a forma de trabalhar das funções do C deixa mais ou menos evidente o seu parentesco com o UNIX. No mundo UNIX, praticamente todos os dispositivos que trocam dados com o sistema operacional o fazem por meio de um descritor de arquivo, sobre o qual operam as chamadas read() e write(). Cabe a cada device driver instalado no sistema operacional a implementação dessas funções com as características necessárias para interagir corretamente com o respectivo dispositivo controlado. Uma vez que o driver as implemente, o usuário do sistema pode tratar dispositivos significativamente distintos de forma relativamente uniforme.

Assim sendo, se, além de programar em C, você o estiver fazendo num sistema UNIX-like, é bem possível que exista pouca (ou mesmo nenhuma) "mágica" na implementação da biblioteca do C, já que o device driver do SO já a realizou. No entanto, num sistema em que essa filosofia de que "(quase) tudo se comporta como arquivo" não valer, provavelmente o esforço de compatibilizar um stream de arquivo e um ligado ao console provavelmente passará, pelo menos em parte, para a implementação da biblioteca.

Mas por que você fica surpreso com que um programa pare para esperar por dados do teclado? Você acha que o programa não tem também de parar para esperar pelos dados que ele vai buscar no disco (especialmente se descontarmos cache e buffers)? Ainda que o tempo de espera seja várias ordens de grandeza menor no segundo caso, ele certamente não será zero.



01 02 03



Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts