paulo1205
(usa Ubuntu)
Enviado em 08/05/2015 - 16:07h
Não é bem macete. A questão é que
scanf () é uma função complexa mesmo -- das mais complexas das funções padronizadas junto com a linguagem C. É uma infelicidade quando se trata de ser apresentada -- e mal apresentada -- logo num curso introdutório.
O que você precisa saber sobre entrada e saída é que as funções padronizadas do C tratam entrada e saída de dados como um fluxo contínuo de caracteres, que podem ser letras, algarismos, sinais de pontuação, espaços em branco (incluindo um caráter que representa o fim da linha) ou até mesmo caracteres de controle.
No caso das funções de entrada, elas consomem bytes do fluxo até satisfazerem algum critério, e deixam intocado tudo aquilo que não consumirem. Existem funções para leitura caráter a caráter, como
getchar () e
fgetc (), funções para leitura de blocos de dados, como
fread (), funções para leitura de texto, como
fgets (), e funções que tentam extrair informações a partir de um determinados formatos de dados esperados na entrada, como
scanf ().
scanf é uma abreviação de
scan formatted (data) , ou seja:
procure (dados) formatados . Durante a procura, ela
pode (mas não necessariamente
tem de ) converter parte dos dados lidos e atribuir o resultado a uma ou mais variáveis. O comportamento da função depende da especificação de formato que é passada como parâmetro à função. Por exemplo, eu posso usar o seguinte código para ver se o usuário digitou exatamente o texto "Paulo" seguido do fim de linha, sem ter de guardar esses seis caracteres em lugar nenhum do programa.
int consumed_chars=0;
if(scanf("Paulo%*1[\n]%n", &consumed_chars)==0 && consumed_chars==6){
/* Os próximos 6 caracteres correspondem exatamente a "Paulo\n". */
}
A explicação é a seguinte:
1) A função
scanf () começa a executar pegando o primeiro caráter da entrada e comparando-o com 'P'. Se for igual, prossegue; se não, devolve o caráter lido para o fluxo de entrada, para que possa ser aproveitado pela próxima operação de leitura, e encerra a execução função, já que foi encontrada uma não correspondência entre a entrada e a string de formatação.
2) Pega o próximo caráter da entrada e compara-o com 'a'. Se for igual, prossegue; se não, devolve o caráter lido para o fluxo de entrada, para que possa ser aproveitado pela próxima operação de leitura (mas o caráter 'P' que já tinha sido consumido será perdido), e encerra a execução função, já que foi encontrada uma não-correspondência entre a entrada e a string de formatação.
3) Pega o próximo caráter da entrada e compara-o com 'u'. Se for igual, prossegue; se não, devolve o caráter lido para o fluxo de entrada, para que possa ser aproveitado pela próxima operação de leitura (mas os caracteres que já tinham sido consumidos serão perdidos), e encerra a execução função, já que foi encontrada uma não-correspondência entre a entrada e a string de formatação.
4) Pega o próximo caráter da entrada e compara-o com 'l'. Se for igual, prossegue; se não, devolve o caráter lido para o fluxo de entrada, para que possa ser aproveitado pela próxima operação de leitura (mas os caracteres que já tinham sido consumidos serão perdidos), e encerra a execução função, já que foi encontrada uma não-correspondência entre a entrada e a string de formatação.
5) Pega o próximo caráter da entrada e compara-o com 'o'. Se for igual, prossegue; se não, devolve o caráter lido para o fluxo de entrada, para que possa ser aproveitado pela próxima operação de leitura (mas os caracteres que já tinham sido consumidos serão perdidos), e encerra a execução função, já que foi encontrada uma não-correspondência entre a entrada e a string de formatação.
6) Não dá para fazer o mesmo com o caráter '\n' (marca de fim de linha) porque espaços em branco na string de formatação de
scanf () significa "consuma uma quantidade qualquer (0 ou mais) de espaços em branco". Então eu uso a conversão
%[] , que serve para ler strings formadas pelos caracteres entre colchetes, colocando o '\n' entre os colchetes, mas limitando a largura máxima em 1 ('1'), e mandando suprimir a atribuição dos caracteres lidos durante essa operação ('*'). O efeito final é pegar o próximo caráter da entrada e compará-lo com '\n'. Se foi igual, consome-o; se não, devolve o caráter lido para o fluxo de entrada, para que possa ser aproveitado pela próxima operação de leitura (mas os caracteres que já tinham sido consumidos serão perdidos), e encerra a execução função, já que foi encontrada uma não-correspondência entre a entrada e a string de formatação.
7) A indicação "%n" faz com que o número de caracteres consumidos pela função até o momento seja armazenado no endereço indicado no próximo argumento da função (
&consumed_chars ). É a maneira que eu uso para saber se a string lida no passo 6 teve tamanho zero ou um.
8) Ao encontrar o fim da string de formatação, a função
scanf () necessariamente retorna.
9) Quando
scanf () retorna o valor retornado por ela pode ser
EOF (que geralmente vale -1) ou o número de conversões com atribuição não-suprimida realizadas. No nosso caso, esse número tem de ser zero, pois só há uma conversão ("%[]"), mas a atribuição de valores a ela correspondentes foi suprimida, e "%n", apesar de fazer uma atribuição de valor, não é considerada uma conversão. Se o valor retornado não for zero, certamente ele será
EOF , indicando a ocorrência de um erro de leitura.
10) O valor de
consumed_chars terá sido modificado para 6 se os caracteres
"Paulo\n" tiverem sido lidos. Qualquer outro valor indica que
scanf () consumiu menos bytes do que o esperado, podendo ser por não ter encontrado uma das cinco letras na ordem esperada (qualquer valor menor do que 5), ou por ter faltado a marca de fim de linha o final da leitura (valor 5).
scanf () e suas irmãs são funções muito poderosas, mas também muito complexas. Eu recomendo a você ler com bastante cuidado a documentação dela. A manpage de
scanf () no Linux é muito detalhada, e eu a recomendo como fonte de referência (com uma ressalva: existem algumas funcionalidades além do que prescreve o padrão do C; se você estiver usando outra plataforma, que não Linux com glibc, deve se abster de usar essas extensões).