Parâmetros interessantes do scanf e do printf em C

O scanf pode parecer chato ao ler strings pelo fato de não tratar espaços em branco e não retirar o ENTER do teclado. Seu domínio tem sido, junto com o printf, um dos maiores problemas para quem está aprendendo C. Mas eles são poderosos.

[ Hits: 137.945 ]

Por: Elgio Schlemer em 06/08/2009 | Blog: http://gravatai.ulbra.tche.br/~elgio


O velho e bom printf



O printf, velho "amigo" dos programadores C, é esquisito a primeira vista. Principalmente ao pessoal que está aprendendo lógica, acostumado com algoritmos, demora um pouco para entender seu jeito de ser.

Basicamente o printf do C é dividido em duas partes bem distintas, sendo que pelo menos a primeira delas deve existir.

A primeira parte é chamada de string de formato ou, do inglês, format string. Ela nada mais é do que dizer ao printf que faça alguma coisa.

Exemplo:

printf("Ola");

Isto pode ser interpretado como uma ordem ao printf:
  • imprima a letra 'O';
  • imprima a letra 'l';
  • imprima a letra 'a'.

Dentro da string de formato, algumas sequências especiais de caracteres são comandos, como o '\n' por exemplo:

printf("Ola\n");

Isto pode ser interpretado como uma ordem ao printf:
  • imprima a letra 'O';
  • imprima a letra 'l';
  • imprima a letra 'a'.
  • imprima uma quebra de linha ('\n')

Na verdade sempre que na string de formato for encontrada uma contra barra ('\') ou um porcento ('%') será um comando. Alguns comandos mais usados:
  • \t - imprima uma tabulação
  • \\ - imprima uma contra barra
  • %d - imprima um valor em formato decimal (e como tem gente que confunde o 'd' de double)
  • %c - imprima um caractere
  • %i - imprima um valor no formato inteiro
  • %f - imprima um valor no formato ponto flutuante
  • %% - imprima o caractere %

Se a string de formato tiver um comando que mande imprimir alguma coisa, esta "alguma coisa" deverá ser fornecida. Assim, a segunda parte do printf são os parâmetros que satisfazem a string de formato. Exemplo:

printf ("Val=%d\n", 345);

  • imprima o 'V';
  • imprima o 'a';
  • imprima o 'l';
  • imprima o '=';
  • imprima um número em formato decimal ('%d');
  • imprima uma quebra de linha ('\n')

A segunda parte possui apenas um valor, o 345, que será usado pelo único %d existente.

Para cada comando que necessite de um parâmetro, um valor a mais deve estar na segunda parte. Coisas "estranhas" ocorrem quando se passa parâmetros de menos ou de mais.

Mas o objetivo deste artigo não é explicar o printf. Ele tem muitas outras opções além destas. O objetivo é tratar alguns parâmetros interessantes, pouco explorados pela maioria dos programadores. Começando pelo retorno da função, que praticamente ninguém usa.

O printf retorna um número inteiro, que é justamente a quantidade de caracteres que foram impressos.

tot = printf("Ola %d", 300);

No trecho de código anterior, tot acabará tendo o valor 7, pois será impresso na tela "Ola 300" (sem quebra de linha) e isto totalizaram sete caracteres.

Mas é no quesito "formatação" que o printf mostra do que é capaz.

É possível colocar o número de casas reservadas para a impressão, junto com o printf. Exemplo:

printf("Valor = %5d\n", 3);
printf("Valor = %5d\n", 30);
printf("Valor = %5d\n", 300);
printf("Valor = %5d\n", 3000);
printf("Valor = %5d\n", 30000);

O %5d indica que será impresso um decimal usando cinco casas decimais. As casas não usadas serão preenchidas com espaço em branco. A sequência de comandos anterior irá gerar a seguinte saída:

Valor =     3
Valor =    30
Valor =   300
Valor =  3000
Valor = 30000

Observe que os números ficaram à direita. Caso seja do interesse que o número fique à esquerda, basta colocar um sinal de menos antes:

printf("a=[%5d]\n", 3);
printf("a=[%-5d]\n", 3);

Observe como o número 3 foi ajustado entre os colchetes nas duas versões do printf.

Em se tratando de números, é ainda possível usar o ZERO para preencher os espaços não usados:

printf("a = %05d\n", 3);
printf("a = %05d\n", 30);
printf("a = %05d\n", 300);

Estes parâmetros são até bem conhecidos. Mas o que poucos sabem é que o 5, número de casas, pode ser também uma variável!

Considere este exemplo:

char frases[4][200];
int i;

for (i = 0; i < 4; i++){
    /* lendo 4 frases */
    scanf("%s", frases[i]);
}

for (i = 0; i < 4; i++){
    /* imprimindo formatado as 4 frases */
    printf("%-10s %d\n", frases[i], i);
}

Se o usuário digitar as seguintes frases:

Apenas_Um_Teste
lixo
Vol
hehehe

Será impresso na tela:

Apenas_Um_Teste 0
lixo       1
Vol        2
hehehe     3

Veja que o "Apenas_um_teste" foi maior do que o tamanho 10 inicialmente pensado, por isto ele ficou desformatado.

No entanto, o seguinte código tem um aspecto bem melhor:

char frases[4][200];
int i, m=-1,x;

for (i = 0; i < 4; i++){
    /* lendo 4 frases */
    scanf("%s", frases[i]);
    x = strlen(frases[i]);
    if (x > m)
       m = x;
}

m = m + 1;
for (i = 0; i < 4; i++){
    /* imprimindo formatado as 4 frases */
    printf("%-*s %d\n", m, frases[i], i);
}

Agora o tamanho da maior string digitada é armazenado na variável m. Quando elas foram impressas, usou-se o comando:

printf("%-*s %d\n", m, frases[i], i);

Veja que tem um asterisco no lugar de onde estaria o valor 10. Isto indica ao printf para usar um dos parâmetros no lugar do *.

    Próxima página

Páginas do artigo
   1. O velho e bom printf
   2. Lendo com scanf
   3. Conclusão
Outros artigos deste autor

Autenticação por desafio e resposta no SSH

Sinais em Linux

Estrutura do Iptables

Fundamentos da criptografia assimétrica

255.255.255.0: A matemática das máscaras de rede

Leitura recomendada

Compilando o Mono 2.2 no Ubuntu 8.10

Algum humor e C++ Design Patterns (parte 1)

Criando aplicativos para o iPhone no Linux (sem Xcode e MacOS X)

Programação com números inteiros gigantes

Compilando Templates C++

  
Comentários
[1] Comentário enviado por fachmann em 07/08/2009 - 11:33h

Excelente!
Vai quebrar um galhão!
Abraço.

[2] Comentário enviado por cesar em 07/08/2009 - 11:45h

Boa elgio,

Parabéns, aproveitando vou fazer uma pergunta =P

quando eu tenho um printf assim por exemplo, printf ("Número"); palavras com acento(entre outras) dentro do printf ao executar a impressão saí "suja" com caracter especial (estranho), como posso resolver isto?

[]'s

[3] Comentário enviado por foguinho.peruca em 07/08/2009 - 22:11h

^^''

Bom artigo!

Lembrou os bons e velhos tempos da faculdade, bem no começo, onde a gente aprendia logica e implementa em C ainda....
Uma pergunta: fflush(stdin) "suja" o buffer? Eu jurava que limpava... qdo eu tinha problemas pra ler algo da entrada padrão eu "limpava" com o fflsuh(stdin). Bom, sempre resolveu.... ;)

Jeff

[4] Comentário enviado por elgio em 08/08/2009 - 10:21h

Oi Jeff

Você não me entendeu.
Não quis dizer que o fflush "suja" o buffer. Ele limpa, como disseste.

Quis dizer que esta é uma solução "suja", no sentido de não tão boa. O jeito fácil de se fazer mas que não é a melhor.

Primeiro que o comportamento de fflush é distinto no Dos e no Linux.

Segundo que ao se usar o fflush PARA ESTE PROBLEMA inviabiliza completamente o uso de pipes e redirecionamentos.

Se no DOS tu criou um arquivo dados.txt:

Primeira linha
Segunda Linha
3455
34.5

E execucar:

C:\> teste.exe < dados.txt

O conteúdo o arquivo será jogado para o programa como se tivesse sido digitado.
Com a minha solução ele irá descartar as frases "Primeira linha", "Segunda linha" e irá ler o 3455 para o scanf("%d...

Com fflush ele irá descartar TUDO!

Então fflush é como explodir um prédio porque uma parede precisa ser refeita, entende! Neste caso!

[5] Comentário enviado por thiagoamm em 08/08/2009 - 23:33h

Otimo artigo.
Bastante didatico e aborda algo fundamental.
Parabens!!!

[6] Comentário enviado por inacioalves em 30/10/2009 - 11:03h

Excelente artigo professor Elgio.

Eu particularmente não conhecia estes parâmetros de scanf e printf. Para fazer a leitura de uma por exemplo, eu escrivia minha própria função indicando um caractere de quebra. Vou indicar este artigo para meus alunos usarem no próximo trabalho.

[7] Comentário enviado por tulios em 03/01/2010 - 13:34h

Excelente Elgio!

Parabens...

[8] Comentário enviado por grammaton em 03/03/2010 - 10:37h

Amigo, bom dia.

Preciso criar um prompt para uma aplicação no console do linux, mas usando o scanf quando aperto as teclas direcionais aparece lixo com :
^[[C^[[A^[[D^[[B

Tem como fazer o scanf não imprimir esse lixo, ou funcionar como um prompt normal de console, isto é, ao pressionar as teclas o cursor mover pelo texto digitado.

Caso não seja possivel via scanf, poderia me indicar alguma lib? Pode ser em C ou C++.

Grato

[9] Comentário enviado por mbcm94 em 14/10/2011 - 17:19h

scanf("%VARIAVEL[^\n]s", frase);

Boa tarde, gostaria de saber se é possivel passar uma variavel dentro da string parametro do scanf.

[10] Comentário enviado por elgio em 14/03/2013 - 21:00h


[9] Comentário enviado por mbcm94 em 14/10/2011 - 17:19h:

scanf("%VARIAVEL[^\n]s", frase);

Boa tarde, gostaria de saber se é possivel passar uma variavel dentro da string parametro do scanf.


Não, não é.


Contribuir com comentário




Patrocínio

Site hospedado pelo provedor HostGator.
Linux banner
Linux banner
Linux banner

Destaques

Artigos

Dicas

Viva o Android

Tópicos

Top 10 do mês

Scripts