Formatando Saída com iomanip

por Fabio A. Mazzarino

Já vi muita gente que sabe orientação a objetos, programa bem em C++, usa STL, mas na hora de formatar a saída utiliza printf.

Quem aprendeu C antes de aprender C++ normalmente prefere utilizar printf para formatar a saída. Com printf vai bem, mas e se a saída for num arquivo? E se os padrões de desenvolvimento da equipe exigir a utilização de std::cout? Pra isso que devemos conhecer as funções de formatação do iomanip.

Fazendo Errado

Vamos começar com um exemplo corriqueiro: datas. É comum termos que formatar a data no formato YYYY-MM-DD, o que sigifica que dia 7 de Setembro de 1822 será formatado da seguinte forma: 1822-09-07. E pra isso o código abaixo não funciona:

unsigned year = 1822;
unsigned month = 9;
unsigned mday = 7;
std::cout << year << "-" << month << "-" << mday << std::endl;

A saída será:

1822-9-7

Que não é o formato esperado

Acertando em C e C++

Quem está acostumado com printf vai dar a seguinte solução:

printf("%04d-%02d-%02d\n", year, month, mday);

Mas nem sempre é viável utilizar printf. Então temos que utilizar os formatadores de iostream.

std::cout << setfill('0') << 
    setw(4) << year << "-" <<
    setw(2) << month << "-" <<
    setw(2) << mday << std::endl;

Tanto o printf, como o cout acima entregam o mesmo resultado:

1822-09-07

Modificadores Comuns

Os modificadores mais comuns são os seguintes:

  • std::dec, std::oct, std::hex – base numérica para formatação
  • std::endl – caracter de fim de linha (normalmente ‘\n’)
  • std::ends – caracter de fim de string (normalmente ‘\0)
  • std::fixed, std::scientific, std::setprecision – formatação de casas de ponto flutuante
  • std::left, std::right – alinhamento à esquerda ou à direita, respectivamente
  • std::setw – tamanho da formatação

Vamos a um exemplo, que tal uma tabela de preços?

std::map<std::string, double> listings;
listings["Sabonete"] = 0.75;
listings["Shampoo"] = 7.59;
listings["Condicionador"] = 7.09;
listings["Odorizador"] = 11.12;
	
std::cout << "*** Tabela de Precos ***" << std::endl;
for (
    std::map<std::string, double>::iterator it = listings.begin(); 
    it != listings.end(); 
    it++
) 
    std::cout << 
        std::setfill('.') << std::setw(20) << std::left << it->first << 
        " $ " << std::setfill(' ') << std::setw(8) << std::right << 
        std::fixed << std::setprecision(2) << it->second << std::endl;

A saída será:

*** Tabela de Precos ***
Condicionador....... $     7.09
Odorizador.......... $    11.12
Sabonete............ $     0.75
Shampoo............. $     7.59

Poderíamos, inclusive ter um pouco mais de capricho e controlar, em tempo de execução, qual a largura do preenchimento com . entre o nome do produto e o preço. Ou ainda calcular qual o espaço entre $ e os números. Mas aí fica pra cada um tentar fazer algo mais bacana.]

Sempre que aplicável todo post conta com um código de exemplo. O post de hoje pode ser encontrado no github.