A função scanf da biblioteca stdio é complexa e difícil de usar. Eric Roberts (The Art and Science of C) escreveu algumas funções "de entrada" mais amistosas. Essas funções estão na biblioteca simpio.
A principal função da biblioteca simpio é ReadLline, que é capaz de ler uma linha de comprimento arbitrário. Veja a seguir uma versão ligeiramente adaptada da função, que chamaremos readLine. Ela substitui, com grande vantagem, a função fgets da biblioteca stdio.
#include <string.h>
#include <stdlib.h>
typedef char *string;
/* A função readLine lê uma linha de texto a partir da
// posição corrente do arquivo infile e devolve o conteúdo
// da linha como uma string. O caractere '\n' que sinaliza
// o fim da linha não é armazenado como parte da string.
// A função readLine devolve NULL se a posição corrente
// estiver no fim do arquivo. Uso típico da função:
// s = readLine (infile);
// Nota de implementação: A função transfere caracteres
// do arquivo infile para um buffer alocado dinamicamente.
// Se o buffer ficar cheio antes que o fim da linha tenha
// sido atingido, um novo buffer é alocado com o dobro do
// tamanho do anterior.
// Esta função foi copiada, com adaptações, da biblioteca
// simpio de Eric Roberts.
*/
string readLine (FILE *infile)
{
string line, nline;
int n, ch, size;
n = 0;
size = 120;
line = mallocX (size + 1);
while ((ch = getc (infile)) != '\n' && ch != EOF) {
if (n == size) {
size *= 2;
nline = (string) mallocX (size + 1);
strncpy (nline, line, n);
free (line);
line = nline;
}
line[n++] = ch;
}
if (n == 0 && ch == EOF) {
free (line);
return NULL;
}
line[n] = '\0';
nline = (string) mallocX (n + 1);
strcpy (nline, line);
free (line);
return nline;
}
É claro que a função readLine supõe, implicitamente, que a linha lida não contém caracteres nulos; essa hipótese é perfeitamente razoável.
Para ler uma linha do teclado, basta invocar readLine (stdin). Se você tem certeza de que a linha de texto terá menos que 100 caracteres, pode se contentar com a seguinte versão simplificada de readLine:
string getLine (void) {
string line;
int n, ch;
n = 0;
line = mallocX (100+1);
while ((ch = getc (stdin)) != '\n') {
if (n >= 100) {
free (line);
exit (EXIT_FAILURE);
}
line[n++] = ch;
}
line[n] = '\0';
return line;
}
A função getInteger extrai um número inteiro de uma linha digitada no teclado. Ela usa a função readLine discutida acima e a função sscanf da biblioteca stdio.
/* A função getLine lê uma linha de texto do teclado,
// extrai dela um número inteiro e devolve o inteiro
// extraído. Se não puder extrair um inteiro ou se a linha
// contiver um ou mais caracteres não brancos depois do
// número na linha, o usuário tem a oportunidade de tentar
// de novo. Uso típico:
// i = getInteger();
// Note de implementação: A função lê uma linha e em seguida
// usa sscanf para extrair dela um inteiro. Ler uma linha
// completa é essencial para que a função possa se recobrar
// de um eventual erro de digitação do usuário; em caso
// contrário, os caracteres depois da ocorrência do erro
// permaneceriam no buffer de entrada e confundiriam as
// operações de entrada subsequentes. A função sscanf admite
// caracteres brancos -- mas nenhum outro tipo de caractere
// -- antes e depois do número inteiro.
// Esta função foi copiada, com adaptações, da biblioteca
// simpio de Eric Roberts.
*/
int getInteger (void)
{
string line;
int value;
char termch;
while (TRUE) {
line = readLine (stdin);
switch (sscanf (line, " %d %c", &value, &termch)) {
case 1:
free (line);
return value;
case 2:
printf ("Caractere inesperado: '%c'\n", termch);
break;
default:
printf ("Digite um inteiro, por favor\n");
break;
}
free (line);
printf ("Tente de novo: ");
}
}
Esta função pode ser facilmente adaptada para ler um long int ou um double.