PHP printf e sprintf

Mostrar 8.5 como 8.50, o número 7 como 007, ou alinhar uma tabela de notas em colunas: com interpolação e concatenação isso dá trabalho. As funções printf e sprintf recebem um modelo com "lacunas formatadas" e preenchem cada lacuna do jeito que você pediu.

Você vai aprender
  • A diferença entre printf (mostra) e sprintf (devolve o texto).
  • Os marcadores: %s, %d, %f, %.2f, %x, %b, %e, %%.
  • Largura e alinhamento: %5d, %-10s, %05d.
  • Argumentos numerados: %1$s, %2$s.
  • Por que o %f sai com ponto, e não com vírgula.
  • Como montar uma tabela alinhada com as notas dos alunos.

A ideia: um carimbo com espaços para preencher

Imagine um carimbo de recibo: "Recebi de        a quantia de R$     ". Cada espaço tem um formato: um é para nome, outro é para valor com centavos. O printf funciona assim: você escreve o modelo uma vez, com marcadores % no lugar das lacunas, e passa os valores na ordem.

O nome vem da linguagem C: print formatted, "imprimir formatado". O s de sprintf é de string: "formatar e devolver como texto".


printf e sprintf

<?php
$nome = "Ana";
$idade = 29;
printf("%s tem %d anos.\n", $nome, $idade);
$frase = sprintf("%s tem %d anos.", $nome, $idade);
echo $frase, "\n";
printf(…)
Monta o texto e mostra na tela, como um echo.
"%s tem %d anos.\n"
O modelo (em inglês, format string). Cada % seguido de uma letra é uma lacuna.
%s
Lacuna para um texto (string). Recebe o 1º valor, $nome.
%d
Lacuna para um número inteiro (decimal, "na base 10"). Recebe o 2º valor, $idade.
$nome, $idade
Os valores, na ordem das lacunas.
sprintf(…)
Faz o mesmo, mas não mostra: devolve o texto pronto, para você guardar numa variável, juntar com outro texto ou passar para outra função.
$ php primeiro.php
Ana tem 29 anos.
Ana tem 29 anos.

Na prática, sprintf é o mais útil dos dois: você formata o valor e depois o usa onde quiser (num heredoc, num HTML, num arquivo). printf é um atalho para echo sprintf(…).


Os marcadores principais

%s, %d e %f

<?php
printf("[%s]\n", 3.14159);
printf("[%d]\n", 3.99);
printf("[%d]\n", "42 laranjas");
printf("[%f]\n", 3.14159);
printf("[%.2f]\n", 3.14159);
printf("[%.1f]\n", 8.75);
$ php marcadores.php
[3.14159]        « %s: o valor como texto, do jeito que está
[3]              « %d: só a parte inteira (corta, não arredonda!)
[42]             « %d: pegou o número do começo do texto
[3.141590]       « %f: sempre 6 casas decimais
[3.14]           « %.2f: 2 casas decimais, arredondando
[8.8]            « %.1f: 1 casa

Os colchetes [ ] estão no modelo só para você ver onde o valor começa e termina. O mais usado de todos é o %.2f: "número com 2 casas decimais", perfeito para dinheiro e notas.

%
Começa uma lacuna.
.2
A precisão: quantas casas depois do ponto.
f
O tipo: número com casas decimais (float).
%d corta, não arredonda

printf("%d", 3.99) mostra 3. Se você quer arredondar para inteiro, use %.0f ou a função round (veja Formatar Números).

O %f não usa vírgula

O laboratório da Ana está em português do Brasil (LANG=pt_BR.UTF-8), e mesmo assim:

$ echo $LANG
pt_BR.UTF-8
$ php -r 'printf("%.2f\n", 1234.5);'
1234.50

Ponto decimal e nenhum separador de milhar. O PHP não usa o idioma do sistema para formatar números: o printf sempre sai no formato americano. (Quem vem do Java estranha: lá o printf em pt_BR dá 1234,50.)

Para o formato brasileiro 1.234,50, a ferramenta certa é o number_format, assunto da próxima aula. Existe uma função setlocale que, em algumas instalações, faz o %f minúsculo usar vírgula; mas ela depende de o idioma estar instalado no servidor (no laboratório da Ana, não fez diferença) e muda o comportamento do programa inteiro. Se quiser garantir o ponto em qualquer situação (num arquivo CSV ou JSON, por exemplo), use %F maiúsculo, que nunca olha o idioma.

Missão: a Ana vendeu 3 kg de maçã a R$ 4,50 o quilo. Mostre Total: R$ 13.50 (com duas casas) usando printf e o marcador %.2f.

<?php
$preco = 4.5;
$quilos = 3;
$total = $preco * $quilos;
echo "Total: R$ $total";
<?php
$preco = 4.5;
$quilos = 3;
$total = $preco * $quilos;
printf("Total: R$ %.2f", $total);

Largura, alinhamento e zeros

Entre o % e a letra, você pode dizer a largura mínima do campo. O valor é completado com espaços até essa largura: à esquerda (alinhado à direita) por padrão, ou à direita com um - (alinhado à esquerda).

<?php
printf("[%5d]\n", 42);
printf("[%-5d]\n", 42);
printf("[%05d]\n", 42);
printf("[%10s]\n", "Ana");
printf("[%-10s]\n", "Ana");
printf("[%'.10s]\n", "Ana");
printf("[%8.2f]\n", 3.14159);
printf("[%08.2f]\n", 3.14159);
printf("[%+d] [%+d]\n", 5, -5);
printf("[%.3s]\n", "Conceição");
$ php largura.php
[   42]          « largura 5, alinhado à direita
[42   ]          « o - alinha à esquerda
[00042]          « o 0 completa com zeros
[       Ana]
[Ana       ]
[.......Ana]     « '. (aspa simples + caractere) escolhe o caractere de preenchimento
[    3.14]       « largura 8 no total (contando o ponto), 2 casas
[00003.14]
[+5] [-5]        « o + mostra o sinal sempre
[Con]            « em %s, o .3 corta o texto em 3 caracteres
MarcadorPartesResultado com 42 / "Ana"
%5dlargura 5   42
%-5d- (à esquerda) + largura 542   
%05d0 (zeros) + largura 500042
%-10s- + largura 10, textoAna       
%8.2flargura 8 + precisão 2   42.00

A ordem das partes é sempre: % → opções (-, 0, +, 'x) → largura → .precisão → tipo. Se o valor for maior que a largura, nada é cortado: a largura é só um mínimo.


A tabela de notas da Ana

Juntando largura e precisão, a Ana monta o boletim da turma com as colunas alinhadas:

<?php
printf("%-10s %6s\n", "Aluno", "Nota");
printf("%-10s %6.2f\n", "Bruno", 8.5);
printf("%-10s %6.2f\n", "Carla", 10);
printf("%-10s %6.2f\n", "Davi", 6.75);
printf("%-10s %6.2f\n", "Eduarda", 9.333);
%-10s
O nome, alinhado à esquerda, ocupando 10 posições.
(espaço)
Um espaço fixo entre as colunas.
%6.2f
A nota, alinhada à direita em 6 posições, com 2 casas. Assim os pontos decimais ficam um embaixo do outro.
%6s
No cabeçalho, o título "Nota" também ocupa 6 posições, alinhado à direita, para ficar em cima dos números.
$ php boletim.php
Aluno        Nota
Bruno        8.50
Carla       10.00
Davi         6.75
Eduarda      9.33

Quando você aprender o foreach, vai fazer o mesmo com uma lista de alunos de qualquer tamanho, escrevendo o printf uma vez só.

Acentos desalinham a tabela
$ php -r 'printf("%-10s %6.2f\n", "Conceição", 7);'
Conceição   7.00

A largura do printf conta bytes, como o strlen. "Conceição" tem 9 letras, mas 11 bytes, e passou da largura 10: nenhum espaço de preenchimento foi colocado e a nota ficou fora da coluna. Para nomes com acento, prepare a coluna com mb_str_pad e use %s: printf("%s %6.2f\n", mb_str_pad($nome, 10), $nota). Veja Acentos e mbstring.

Experimente: a tabela de preços da lojinha está desalinhada. Use %-10s para o nome e %5.2f para o preço em cada linha. Repare que "Maçã" tem acento: veja o que acontece e corrija a primeira linha com mb_str_pad.

<?php
printf("%s %s\n", "Maçã", 4.5);
printf("%s %s\n", "Uva", 7.9);
printf("%s %s\n", "Banana", 3.25);
<?php
printf("%s %5.2f\n", mb_str_pad("Maçã", 10), 4.5);
printf("%-10s %5.2f\n", "Uva", 7.9);
printf("%-10s %5.2f\n", "Banana", 3.25);

Outros marcadores: %x, %b, %e, %%

<?php
printf("[%x] [%X] [%o] [%b]\n", 255, 255, 8, 5);
printf("[%08b]\n", 5);
printf("[%e]\n", 1234.5678);
printf("[%.2e]\n", 0.000123);
printf("[%c]\n", 65);
printf("Aprovação: %d%%\n", 87);
$ php outros.php
[ff] [FF] [10] [101]   « hexadecimal, hexadecimal maiúsculo, octal, binário
[00000101]             « binário com 8 dígitos
[1.234568e+3]          « notação científica: 1,234568 × 10³
[1.23e-4]              « 1,23 × 10⁻⁴
[A]                    « o caractere de código 65
Aprovação: 87%         « %% = um sinal de porcentagem
MarcadorMostraQuando aparece
%x / %XHexadecimal (base 16)Cores do CSS (#ff8800), códigos
%oOctal (base 8)Permissões de arquivo (755)
%bBinário (base 2)Estudar bits
%eNotação científicaNúmeros muito grandes ou muito pequenos
%cO caractere com aquele códigoRaro
%%Um % literalPorcentagens

O %% é importante: como o % começa uma lacuna, para escrever o símbolo você precisa de dois. Um exemplo com cor: sprintf("#%02x%02x%02x", 255, 136, 0) dá #ff8800.


Argumentos numerados: %1$s

Normalmente, as lacunas pegam os valores na ordem. Com %N$, você escolhe qual valor vai em cada lacuna: %1$s é "o 1º valor, como texto", %2$s é "o 2º". Assim dá para trocar a ordem ou repetir um valor.

<?php
printf('%2$s, %1$s' . "\n", "Ana", "Souza");
printf('%1$s! %1$s! %1$s!' . "\n", "Bis");
printf('%2$s tirou %1$.1f' . "\n", 9.5, "Ana");
$ php numerados.php
Souza, Ana             « o 2º antes do 1º
Bis! Bis! Bis!         « o mesmo valor três vezes
Ana tirou 9.5          « %1$.1f: o 1º valor, com 1 casa
Aspas simples no modelo com $

O $ do %1$s dentro de aspas duplas seria lido como o começo de uma variável (veja Interpolação). Por isso o modelo está entre aspas simples, e o "\n" foi concatenado à parte. Com aspas duplas, escreva "%1\$s".

Esse recurso é muito usado em sites traduzidos: em português é "Souza, Ana", em outro idioma pode ser "Ana Souza", e só o modelo muda.


Formas diferentes e qual usar

FunçãoO que fazQuando usar
printf($modelo, …)Formata e mostraProgramas de terminal, relatórios
sprintf($modelo, …)Formata e devolve o textoQuase sempre: guardar, montar HTML, juntar
vsprintf($modelo, $lista)Igual ao sprintf, mas os valores vêm num arrayQuando os valores já estão numa lista
fprintf($arquivo, $modelo, …)Formata e grava num arquivoRelatórios em arquivo (veja Arquivos)
number_format($n, 2, ",", ".")Formata um número no padrão brasileiroValores para o usuário ver: 1.234,50 (veja Formatar Números)
Interpolação "…$var…"Coloca o valor como estáQuando não precisa de formatação

Quando dá errado

ArgumentCountError: 3 arguments are required, 2 given
<?php
printf("%s tem %d anos\n", "Ana");
$ php faltou.php

Fatal error: Uncaught ArgumentCountError: 3 arguments are required, 2 given in /home/ana/php/faltou.php:2
Stack trace:
#0 /home/ana/php/faltou.php(2): printf('%s tem %d anos\n', 'Ana')
#1 {main}
  thrown in /home/ana/php/faltou.php on line 2

O que significa: "são necessários 3 argumentos, 2 foram dados". O modelo tem duas lacunas, então o printf precisa de 3 argumentos (o modelo + 2 valores), mas só recebeu o modelo e "Ana". Erro fatal: o programa para.

Como consertar: conte os % do modelo (sem contar os %%) e passe um valor para cada. Com argumentos numerados, o maior número manda: '%2$s' exige pelo menos 2 valores.

Um % sozinho no texto
<?php
printf("Desconto de 10% para Ana\n");
$ php porcento.php

Fatal error: Uncaught ArgumentCountError: 2 arguments are required, 1 given in /home/ana/php/porcento.php:2
Stack trace:
#0 /home/ana/php/porcento.php(2): printf('Desconto de 10%...')
#1 {main}
  thrown in /home/ana/php/porcento.php on line 2

O que significa: a Ana queria escrever "10%", mas para o printf todo % começa uma lacuna. Ele leu % p como uma lacuna (o espaço é uma opção de preenchimento) e foi buscar um valor que não existe. Por isso o erro fala de argumentos, e não do sinal.

Como consertar: escreva 10%%.

ValueError: Unknown format specifier
<?php
printf("Idade: %i anos\n", 29);
$ php tipo.php

Fatal error: Uncaught ValueError: Unknown format specifier "i" in /home/ana/php/tipo.php:2
Stack trace:
#0 /home/ana/php/tipo.php(2): printf('Idade: %i anos\n', 29)
#1 {main}
  thrown in /home/ana/php/tipo.php on line 2

O que significa: "marcador de formato desconhecido: i". Em outras linguagens (C, Java) %i é inteiro; no PHP, não existe.

Como consertar: use um dos marcadores da referência: para inteiro, %d.

A nota "8,5" virou 8.00 (sem nenhum aviso)
<?php
$nota = "8,5";
printf("Nota: %.2f\n", $nota);
$ php virgula.php
Nota: 8.00

O que significa: para o PHP, números usam ponto. O texto "8,5" foi lido até onde parecia número (8) e o resto foi ignorado, sem aviso. Isso acontece muito com valores digitados por brasileiros num formulário.

Como consertar: troque a vírgula antes: $nota = (float) str_replace(",", ".", $nota);. Validar o que o usuário digita é o assunto de Validar Dados.

Apareceu Array no lugar do valor
<?php
$notas = [8.5, 9];
printf("Notas: %s\n", $notas);
$ php lista.php

Warning: Array to string conversion in /home/ana/php/lista.php on line 3
Notas: Array

Como consertar: passe um item ($notas[0]) ou junte a lista antes com implode(", ", $notas). Se os valores já estão numa lista, na ordem certa, use vsprintf.


Referência

MarcadorTipoExemplo → resultado
%sTextosprintf("%s", "Ana") → Ana
%dInteiro (corta as casas)sprintf("%d", 3.99) → 3
%fDecimal, 6 casassprintf("%f", 1.5) → 1.500000
%.2fDecimal, 2 casassprintf("%.2f", 1.5) → 1.50
%FDecimal, nunca usa o idiomasprintf("%.1F", 1.5) → 1.5
%5d / %-5dLargura 5, à direita / à esquerda[   42] / [42   ]
%05dZeros à esquerdasprintf("%05d", 42) → 00042
%'*8sPreenche com *sprintf("%'*8s", "Ana") → *****Ana
%+dSempre com sinalsprintf("%+d", 5) → +5
%.3sCorta o texto (em bytes)sprintf("%.3s", "Ana Souza") → Ana
%x / %X / %o / %bHexa / HEXA / octal / bináriosprintf("%b", 5) → 101
%eNotação científicasprintf("%.2e", 0.000123) → 1.23e-4
%cCaractere pelo códigosprintf("%c", 65) → A
%%O símbolo %sprintf("%d%%", 87) → 87%
%1$s, %2$.2fArgumento numeradosprintf('%2$s %1$s', "a", "b") → b a

Exercícios

1. Duas casas

Complete para mostrar 9.50:

printf("", 9.5);

2. Matrícula com zeros

Complete para o número 42 virar 000042:

echo sprintf("", 42);

3. Porcentagem

Complete para mostrar Desconto: 15%:

printf("Desconto: ", 15);

4. Qual a diferença entre printf e sprintf?

5. O que mostra printf("%d", 7.9)?

6. O laboratório está em pt_BR. O que mostra printf("%.2f", 2.5)?

Missão final: complete o boletim para as notas ficarem alinhadas embaixo de "Nota", como na tabela da aula: nome com %-10s e nota com %6.2f. A saída deve ficar igual à do boletim da aula.

<?php
printf("%-10s %6s\n", "Aluno", "Nota");
printf("%s %s\n", "Carla", 10);
printf("%s %s\n", "Davi", 6.75);
<?php
printf("%-10s %6s\n", "Aluno", "Nota");
printf("%-10s %6.2f\n", "Carla", 10);
printf("%-10s %6.2f\n", "Davi", 6.75);

Resumo

  • printf mostra; sprintf devolve o texto. Os dois recebem um modelo com lacunas % e os valores na ordem.
  • %s texto, %d inteiro (corta), %f decimal, %.2f duas casas (arredonda), %% o símbolo de porcentagem.
  • Largura: %5d (à direita), %-10s (à esquerda), %05d (zeros). A largura conta bytes: acentos desalinham.
  • %1$s escolhe qual argumento usar; escreva o modelo com aspas simples.
  • O %f sai com ponto, mesmo com o sistema em português. Para 1.234,50, use number_format.
  • Faltou valor para uma lacuna? ArgumentCountError. Um % solto? Unknown format specifier.