paulo1205
(usa Ubuntu)
Enviado em 05/06/2013 - 17:19h
A forma mais simples e segura de ler strings em C é usar
fgets ().
gets () é uma função obsoleta, e foi inclusive removida da última revisão do padrão do C (em 2011). O problema com ela era justamente não limitar a quantidade de caracteres lidos, o que dava margem a exceder o tamanho da string que deveria guaradar os dados.
gets () deve ser evitada a todo custo.
Dá para ler strings com espaços através de
scanf (), usando a conversão do tipo "
%[...] " em lugar de "
%s " (
scanf () é muito poderosa, mas um bocado complexa; é importante gastar um tempo razoável lendo sua documentação). Eu prefiro usar
fgets (), mas uma forma de fazer com
scanf (), usando somente '\n' como terminador da string, é mais ou menos como a seguinte.
char str[80];
int n, a, b;
/* ... */
/* Lê string, verificando se a leitura foi bem sucedida. */
a=b=0;
n=scanf("%79[^\n]%n%*1[\n]%b", str, &a, &b);
str[79]=0; /* Garante o byte nulo terminador da string. */
if(n==EOF){
/*
Leitura falhou.
Provavelmente você vai colocar algum código de tratamento de erro,
talvez querendo testar os valores de feof() e ferror().
*/
}
else if(n>0){
/* Conseguiu ler uma string não-vazia. */
/*
Verifica se ocorreu truncamento da string antes da marca de fim
de linha (i.e., a linha digitada tem mais do que 79 caracteres).
*/
if(b>a){
/*
Não, a linha não foi truncada. Todos os caracteres digitados foram
consumidos pelo scanf(), incluindo a marca de fim de linha.
*/
}
else{
/*
A linha foi truncada. Isso significa que os caracteres excedentes
-- incluindo a marca de fim de linha -- ainda estão no buffer, es-
perando ser lidos por alguma outra função.
*/
}
}
else{
/*
n==0. Isso provavelmente significa que o usuário digitou ENTER sem
ter digitado antes qualquer outro caráter. Nesse caso a marca de fim
linha vai permanecer no buffer de entrada, devendo ser consumida de
algum modo, para não impactar leituras futuras.
*/
}
Parece complicado? Pois é mesmo, especialmente se você quiser programar de modo seguro. Uma vantagem desse método é que o valor de
a já dá, de cara, o tamanho da string lida, permitindo que se evite uma chamada a
strlen () para medir o tamanho da string.
Com
fgets (), o sistema lê a linha -- incluindo a quebra de linha --, e a armazena no buffer. O motivo de armazenar a quebra de linha é dar a aplicação uma forma de determinar se houve truncamento da linha. A forma de fazer seria mais ou menos a seguinte.
#define BUFFER_LEN 80
char buf[BUFFER_LEN];
int len;
if(fgets(buf, BUFFER_LEN, stdin)!=NULL){
/* Leitura bem sucedida. */
buf[BUFFER_LEN-1]=0; /* Garante o byte nulo no final do buffer. */
len=strlen(buf); /* Mede o comprimento efetivo da string lida. */
if(len>0){
if(buf[len-1]=='\n'){
/* Linha lida completamente e sem truncamento. */
/*
Se a quebra de linha não interessar, pode-se trocá-la pelo byte
nulo que marca o fim da string, fazendo ``buf[len-1]=0;''. Pode
ser, porém, que, se len==1, o resultado de remover o fim de linha
é que o buffer fique com tamanho zero.
*/
}
else{
/*
A linha foi truncada. Como proceder nesse caso depende da aplicação.
No entanto, os caracteres não lidos (incluindo, como sempre, a quebra
de linha) vão permanecer no buffer.
*/
}
}
else{
/*
Acho que este caso (len==0) não deve ocorrer nunca, a não ser
que BUFFER_LEN valha 1 (o que provavelmente seria uma tremenda
bobagem).
*/
}
}
else{
/* A leitura falhou completamente. Dá o devido tratamento. */
}