Sprintf equivalente em Java

284

Printf foi adicionado ao Java com a versão 1.5, mas não consigo encontrar como enviar a saída para uma string em vez de um arquivo (que é o que o sprintf faz em C). Alguém sabe como fazer isso?

cavalo de papel
fonte

Respostas:

473
// Store the formatted string in 'result'
String result = String.format("%4d", i * j);

// Write the result to standard output
System.out.println( result );

Veja o formato e sua sintaxe

Eugene Yokota
fonte
28

Strings são tipos imutáveis. Você não pode modificá-los, retorne apenas novas instâncias de string.

Por isso, formatar com um método de instância faz pouco sentido, pois teria que ser chamado como:

String formatted = "%s: %s".format(key, value);

Os autores Java originais (e autores .NET) decidiram que um método estático fazia mais sentido nessa situação, pois você não está modificando o destino, mas chamando um método de formato e transmitindo uma sequência de entrada.

Aqui está um exemplo de por format()que seria burro como um método de instância. No .NET (e provavelmente em Java), Replace()é um método de instância.

Você consegue fazer isso:

 "I Like Wine".Replace("Wine","Beer");

No entanto, nada acontece, porque as strings são imutáveis. Replace()tenta retornar uma nova string, mas não está atribuída a nada.

Isso causa muitos erros comuns de novatos, como:

inputText.Replace(" ", "%20");

Novamente, nada acontece; em vez disso, você deve fazer:

inputText = inputText.Replace(" ","%20");

Agora, se você entende que as cordas são imutáveis, isso faz todo o sentido. Se não, então você está apenas confuso. O local apropriado para Replace()seria onde format()está, como um método estático de String:

 inputText = String.Replace(inputText, " ", "%20");

Agora não há dúvida sobre o que está acontecendo.

A verdadeira questão é: por que os autores dessas estruturas decidiram que um deveria ser um método de instância e o outro estático? Na minha opinião, ambos são expressos de forma mais elegante como métodos estáticos.

Independentemente da sua opinião, a verdade é que você é menos propenso a cometer um erro ao usar a versão estática, e o código é mais fácil de entender (sem dicas ocultas).

Claro que existem alguns métodos que são perfeitos como métodos de instância, pegue String.Length ()

int length = "123".Length();

Nessa situação, é óbvio que não estamos tentando modificar "123", apenas estamos inspecionando e retornando seu comprimento. Este é um candidato perfeito para um método de instância.

Minhas regras simples para métodos de instância em objetos imutáveis:

  • Se você precisar retornar uma nova instância do mesmo tipo, use um método estático.
  • Caso contrário, use um método de instância.
FlySwat
fonte
4
Vejo que, de alguma forma, você entendeu que eu estava sugerindo que a string de formato fosse modificada. Eu nunca considerei a possibilidade de alguém esperar que uma String mude, pois sua imutabilidade é tão fundamental.
22680
4
Como a string de formato geralmente é mais parecida com "O preço é% 4d" e não "% 4d", ainda vejo muito potencial de confusão. O que você tem contra métodos estáticos? :)
FlySwat 13/10/08
44
Essa resposta parece não ter nada a ver com a pergunta.
Steve McLeod
2
A resposta nem é Java, parece ser mais relevante para o .NET.
Photodeus
3
-1. Downvoted b / c é tangencial. E não necessariamente o melhor estilo para imutáveis. Esse estilo é mais detalhado do que as chamadas simples de método, principalmente para operações encadeadas. E desiste do polimorfismo em tempo de execução porque está chamando métodos estáticos, o que é significativo. YMMV.
Andrew Janke
3

Ambas as soluções funcionam para simular a impressão, mas de uma maneira diferente. Por exemplo, para converter um valor em uma sequência hexadecimal, você tem as 2 soluções a seguir:

  • com format(), o mais próximo de sprintf():

    final static String HexChars = "0123456789abcdef";
    
    public static String getHexQuad(long v) {
        String ret;
        if(v > 0xffff) ret = getHexQuad(v >> 16); else ret = "";
        ret += String.format("%c%c%c%c",
            HexChars.charAt((int) ((v >> 12) & 0x0f)),
            HexChars.charAt((int) ((v >>  8) & 0x0f)),
            HexChars.charAt((int) ((v >>  4) & 0x0f)),
            HexChars.charAt((int) ( v        & 0x0f)));
        return ret;
    }
    
  • com replace(char oldchar , char newchar), um pouco mais rápido, mas bastante limitado:

        ...
        ret += "ABCD".
            replace('A', HexChars.charAt((int) ((v >> 12) & 0x0f))).
            replace('B', HexChars.charAt((int) ((v >>  8) & 0x0f))).
            replace('C', HexChars.charAt((int) ((v >>  4) & 0x0f))).
            replace('D', HexChars.charAt((int) ( v        & 0x0f)));
        ...
    
  • Existe uma terceira solução que consiste em adicionar o caractere retum a um (char são números que se somam !), Como em:

    ...
    ret += HexChars.charAt((int) ((v >> 12) & 0x0f)));
    ret += HexChars.charAt((int) ((v >>  8) & 0x0f)));
    ...
    

... mas isso seria muito feio.

догонят
fonte
Todas as ótimas idéias, mas transforma seu código em código somente de gravação, impossível de entender para seu colega de trabalho.
Ben
0

Você pode fazer um printf para qualquer coisa que seja um OutputStream com um PrintStream. De alguma forma, imprimindo em um fluxo de string:

PrintStream ps = new PrintStream(baos);
ps.printf("there is a %s from %d %s", "hello", 3, "friends");
System.out.println(baos.toString());
baos.reset(); //need reset to write new string
ps.printf("there is a %s from %d %s", "flip", 5, "haters");
System.out.println(baos.toString());
baos.reset();

O fluxo da sequência pode ser criado assim: ByteArrayOutputStream:

ByteArrayOutputStream baos = new ByteArrayOutputStream();
armagedescu
fonte