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.
- A diferença entre
printf(mostra) esprintf(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
%fsai 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
| Marcador | Partes | Resultado com 42 / "Ana" |
|---|---|---|
%5d | largura 5 | 42 |
%-5d | - (à esquerda) + largura 5 | 42 |
%05d | 0 (zeros) + largura 5 | 00042 |
%-10s | - + largura 10, texto | Ana |
%8.2f | largura 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ó.
$ 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
| Marcador | Mostra | Quando aparece |
|---|---|---|
%x / %X | Hexadecimal (base 16) | Cores do CSS (#ff8800), códigos |
%o | Octal (base 8) | Permissões de arquivo (755) |
%b | Binário (base 2) | Estudar bits |
%e | Notação científica | Números muito grandes ou muito pequenos |
%c | O caractere com aquele código | Raro |
%% | Um % literal | Porcentagens |
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
$
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ção | O que faz | Quando usar |
|---|---|---|
printf($modelo, …) | Formata e mostra | Programas de terminal, relatórios |
sprintf($modelo, …) | Formata e devolve o texto | Quase sempre: guardar, montar HTML, juntar |
vsprintf($modelo, $lista) | Igual ao sprintf, mas os valores vêm num array | Quando os valores já estão numa lista |
fprintf($arquivo, $modelo, …) | Formata e grava num arquivo | Relatórios em arquivo (veja Arquivos) |
number_format($n, 2, ",", ".") | Formata um número no padrão brasileiro | Valores 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
<?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.
% 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%%.
<?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.
<?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.
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
| Marcador | Tipo | Exemplo → resultado |
|---|---|---|
%s | Texto | sprintf("%s", "Ana") → Ana |
%d | Inteiro (corta as casas) | sprintf("%d", 3.99) → 3 |
%f | Decimal, 6 casas | sprintf("%f", 1.5) → 1.500000 |
%.2f | Decimal, 2 casas | sprintf("%.2f", 1.5) → 1.50 |
%F | Decimal, nunca usa o idioma | sprintf("%.1F", 1.5) → 1.5 |
%5d / %-5d | Largura 5, à direita / à esquerda | [ 42] / [42 ] |
%05d | Zeros à esquerda | sprintf("%05d", 42) → 00042 |
%'*8s | Preenche com * | sprintf("%'*8s", "Ana") → *****Ana |
%+d | Sempre com sinal | sprintf("%+d", 5) → +5 |
%.3s | Corta o texto (em bytes) | sprintf("%.3s", "Ana Souza") → Ana |
%x / %X / %o / %b | Hexa / HEXA / octal / binário | sprintf("%b", 5) → 101 |
%e | Notação científica | sprintf("%.2e", 0.000123) → 1.23e-4 |
%c | Caractere pelo código | sprintf("%c", 65) → A |
%% | O símbolo % | sprintf("%d%%", 87) → 87% |
%1$s, %2$.2f | Argumento numerado | sprintf('%2$s %1$s', "a", "b") → b a |
Exercícios
1. Duas casas
Complete para mostrar 9.50:
2. Matrícula com zeros
Complete para o número 42 virar 000042:
3. Porcentagem
Complete para mostrar 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
printfmostra;sprintfdevolve o texto. Os dois recebem um modelo com lacunas%e os valores na ordem.%stexto,%dinteiro (corta),%fdecimal,%.2fduas casas (arredonda),%%o símbolo de porcentagem.- Largura:
%5d(à direita),%-10s(à esquerda),%05d(zeros). A largura conta bytes: acentos desalinham. %1$sescolhe qual argumento usar; escreva o modelo com aspas simples.- O
%fsai com ponto, mesmo com o sistema em português. Para1.234,50, usenumber_format. - Faltou valor para uma lacuna?
ArgumentCountError. Um%solto?Unknown format specifier.