Páginas

quarta-feira, 1 de junho de 2011

Formatando Mensagens (Strings) em Java

É comum, durante o desenvolvimento de software, a necessidade de formar mensagens parametrizadas para apresentação ao usuário.

Por exemplo, digamos que você deseja enviar uma mensagem de boas vindas ao usuário após o login: "Olá Claudio". Qual a primeira tentativa de montar esta mensagem:
String mensagem = "Olá " + nome;
Vamos analisar outras formas de codificar este tipo de mensagens?

Uma nova forma

Digamos que a mensagem que você gostaria de mostrar fosse: "Olá Claudio, sua última visita foi em 25/05/11", mantendo o padrão de codificação acima, teríamos:
String mensagem = "Olá " + nome + ", sua última visita foi em " + data;
Nosso código já começa a ficar um pouco confuso, vamos tentar deixá-lo mais elegante? Que tal:
String mensagem = String.format("Olá %s, sua última visita foi em %s",
   nome, data);
Neste trecho de código nós estamos utilizando o método format da classe String (disponível a partir da versão 1.5), passando como primeiro argumento a nossa mensagem, e nos argumentos seguintes as variáveis que serão utilizadas para preencher os espaços previamente determinados na mensagem. Os espaços reservados são definidos com o sinal de porcento (%) seguido das definições de formatação deste espaço. No nosso exemplo foi utilizada uma definição de formatação genérica, tipo texto, onde o valor de preenchimento será obtido do método toString() dos objetos passados como argumentos de preenchimento.

O que é isso?

O método format da classe String é implementado pela classe java.util.Formatter, e outros atalhos comuns são:
System.[out|err].printf()
System.[out|err].format()
Além da formatação de tipo texto, existem também formatações de tipos numérico e data, com especializações para montar mensagens de forma bem abrangente. No nosso exemplo, para especificar o tipo de data a ser exibida, considerando que o objeto data seja um java.util.Date, teríamos:
String.format("Olá %s, sua última visita foi em %tD", nome, data)
* A definição de formatação para data e hora (%t), obriga o uso de especialização.

Reutilizando parâmetros

Nós também podemos reutilizar objetos de parâmetro para preencher diversos espaços definidos na mensagem, a primeira forma de realizar isto seria reaproveitando o parâmetro do último espaço com o símbolo < (menor que):
String.format("Olá %s, sua última visita foi em %td de %<tB de %<tY",
   nome, data)
Resultado: "Olá Claudio, sua última visita foi em 25 de Maio de 2011"

A outra forma de realizar a reutilização de parâmetros é através da identificação do índice do parâmetro com um número seguido do símbolo $ (cifrão):
String.format("Olá %1$s, sua última visita foi em %2$td de %2$tB de %2$tY",
   nome, data)
Resultado: "Olá Claudio, sua última visita foi em 25 de Maio de 2011"

Mas ainda não acabou

Como no lema dos infomerciais...
O String.format() não atendeu as suas necessidades, o sistema de formatação dele é deficiente? Então vamos conhecer outra classe: a java.text.MessageFormat.

Diferente do método String.format() que utiliza uma classe para realizar todas as opções de formatação, a classe MessageFormat é uma classe que faz uso das diversas classes java.text.Format disponíveis no Java, como: java.text.DecimalFormat, java.text.SimpleDateFormat, java.text.ChoiceFormat, etc.

Uma diferença importante é a forma de definição dos espaços reservados, pois utiliza um padrão mais textual que torna o código mais fácil de ser compreendido. No nosso exemplo, o código ficaria:
MessageFormat.format("Olá {0}, sua última visita foi em {1,date}",
   nome, data);
Utilizando String.format() com muitos padrões de formatação pode facilmente tornar o texto de formatação um verdadeiro emaranhado de símbolos, o que pode ser evitado com o uso de MessageFormat, que utiliza apenas definições textuais.

Vantagens

Este tipo de formatação de mensagens tem grande vantagem - além de código mais limpo - quando se utiliza um sistema de recuperação de textos de mensagens, como por exemplo arquivos properties com localização, ou classes de manutenção de constantes.

Com uso de arquivos de definição isolados do código fonte, é possível, também, permitir ao usuário personalizar as mensagens.

E desvantagens

Normalmente estes tipos de formatação de mensagens são mais custosos do que concatenações de Strings inline ou o uso de StringBuilder.

O que interessa mesmo

Como o que interessa é código de exemplo, aqui vai um exemplo fazendo uma apanhado geral dos formatos mais comuns, e de quebra um exemplo de uso da opção choice do MessageFormat:
import java.text.MessageFormat;
import java.util.Date;
import java.util.Formatter;

public class StringFormat {

 public static void main(String[] args) {

  int int1 = 1;
  int int2 = 2000;
  float float1 = 1.0f;
  float float2 = 2000.02f;
  String string1 = "string1";
  String string2 = null;
  Date data = new Date();
  Object todosArgumentos[] = new Object[]{int1, int2, float1, float2, string1, string2, data};
  Object numeros[] = new Object[]{int1, int2, float1, float2};

  System.out.println(String.format("|%s|%s|%s|%s|%s|%s|%s|", int1, int2, float1, float2, string1, string2, data));
  System.out.println(new Formatter().format("|%d|%d|%f|%f|%s|%s|%tc|", todosArgumentos));
  System.out.printf("|%d|%d|%f|%f|%S|%S|%Tc|%n", todosArgumentos);

  System.out.println("");
  
  System.out.printf("|%10d|%10d|%10f|%20f|%10s|%10s|%30tc|%n", todosArgumentos);
  System.out.format("|%-10d|%-10d|%-10f|%-20f|%-10s|%-10s|%-30tc|\n", todosArgumentos);
  System.out.printf("|%010d|%010d|%010f|%020f|%10s|%10s|%30tT|\n", todosArgumentos);
  System.out.format("|%010d|%010d|%010.2f|%020.4f|%10s|%10s|%30tD|\n", todosArgumentos);
  System.out.printf("|%010d|%,010d|%010.2f|%,020.4f|%10s|%10s|%30tF|\n", todosArgumentos);
  System.out.format("|%10d|%,10d|%10.2f|%,20.4f|%10s|%10s|%30tr|\n", todosArgumentos);

  System.out.println("");
  
  System.out.printf("Data: %td de %<tB de %<tY\n", data);
  System.out.format("Data: %1$td de %1$tB de %1$tY\n", data);
  
  System.out.println("");
  
  System.out.printf("string1(%s=%<S), int2(%d=%<X), float2(%f), string1(%1$s)\n", string1, int2, float2);
  System.out.printf("string1(%s=%1$S), int2(%d=%2$X), float2(%f), string1(%1$s)\n", string1, int2, float2);
  
  System.out.println("");
  
  System.err.println(MessageFormat.format("|{0}|{1}|{2}|{3}|{4}|{5}|{6}|", todosArgumentos));
  System.err.println(MessageFormat.format("|{0,number}|{1,number,integer}|{2,number,percent}|{3,number,currency}|{4}|{5}|{6,date}|{6,time}|", todosArgumentos));

  System.err.println("");
  
  System.err.println(MessageFormat.format("|{0,number,000000}|{1,number,######}|{2,number,00,000.0}|{3,number,#,###.####}|", numeros));

  System.err.println("");
  
  MessageFormat mf = new MessageFormat("{0,choice,0#ninguém|1#uma pessoa|1<{0,number,00} pessoas} aguardando atendimento na sala de emergência");
  for(int i = 10; i >= 0; i--) {
   System.err.println(mf.format(new Object[]{i}));
  }  
 }
}

Nenhum comentário:

Postar um comentário

Olá! Antes de postar seu comentário, por favor, observe que comentários técnicos, elogios e sugestões são antecipadamente agradecidos, esclarecimentos sobre os conceitos envolvidos na postagem serão respondidos da melhor forma possível, mas pedidos de ajuda técnica ou suporte individual deverão ser feitos através do formulário de contato. Grato!