Sprintf equivalente en Java

284

Printf se agregó a Java con la versión 1.5, pero parece que no puedo encontrar cómo enviar la salida a una cadena en lugar de a un archivo (que es lo que hace sprintf en C). ¿Alguien sabe como hacer esto?

caballo de papel
fuente

Respuestas:

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

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

Ver formato y su sintaxis

Eugene Yokota
fuente
28

Las cadenas son tipos inmutables. No puede modificarlos, solo devuelve nuevas instancias de cadena.

Debido a eso, el formateo con un método de instancia tiene poco sentido, ya que debería llamarse así:

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

Los autores originales de Java (y los autores de .NET) decidieron que un método estático tenía más sentido en esta situación, ya que no está modificando el objetivo, sino que llama a un método de formato y pasa una cadena de entrada.

Aquí hay un ejemplo de por qué format()sería tonto como método de instancia. En .NET (y probablemente en Java), Replace()es un método de instancia.

Puedes hacerlo:

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

Sin embargo, no pasa nada, porque las cadenas son inmutables. Replace()intenta devolver una nueva cadena, pero no está asignada a nada.

Esto causa muchos errores comunes de novatos como:

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

Nuevamente, no pasa nada, en su lugar debes hacer:

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

Ahora, si comprende que las cadenas son inmutables, eso tiene mucho sentido. Si no lo haces, entonces estás confundido. El lugar adecuado para Replace()sería donde format()está, como método estático de String:

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

Ahora no hay dudas sobre lo que está pasando.

La verdadera pregunta es, ¿por qué los autores de estos marcos decidieron que uno debería ser un método de instancia y el otro estático? En mi opinión, ambos se expresan con más elegancia como métodos estáticos.

Independientemente de su opinión, la verdad es que es menos propenso a cometer un error al usar la versión estática, y el código es más fácil de entender (sin problemas ocultos).

Por supuesto, hay algunos métodos que son perfectos como métodos de instancia, tome String.Length ()

int length = "123".Length();

En esta situación, es obvio que no estamos tratando de modificar "123", solo lo estamos inspeccionando y devolviendo su longitud. Este es un candidato perfecto para un método de instancia.

Mis reglas simples para los métodos de instancia en objetos inmutables:

  • Si necesita devolver una nueva instancia del mismo tipo, use un método estático.
  • De lo contrario, use un método de instancia.
FlySwat
fuente
44
Veo que de alguna manera se te ocurrió la idea de que estaba sugiriendo que se modificara la cadena de formato. Nunca consideré la posibilidad de que alguien pueda esperar que cambie una Cadena, ya que su inmutabilidad es tan fundamental.
erickson
44
Dado que la cadena de formato suele ser más como "El precio es% 4d" y no "% 4d", todavía veo un gran potencial de confusión. ¿Qué tienes contra los métodos estáticos? :)
FlySwat
44
Esta respuesta parece no tener nada que ver con la pregunta.
Steve McLeod
2
La respuesta ni siquiera es Java, parece ser más relevante para .NET
Photodeus el
3
-1. Voto b / c rechazado es tangencial. Y no necesariamente el mejor estilo para los inmutables. Este estilo es más detallado que las llamadas a métodos simples, particularmente para operaciones encadenadas. Y abandona el polimorfismo en tiempo de ejecución porque está llamando a métodos estáticos, lo cual es significativo. YMMV.
Andrew Janke
3

Ambas soluciones funcionan para simular printf, pero de una manera diferente. Por ejemplo, para convertir un valor en una cadena hexadecimal, tiene las 2 soluciones siguientes:

  • con format(), más cercano a 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;
    }
  • con replace(char oldchar , char newchar), algo más rápido pero 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)));
        ...
  • Hay una tercera solución que consiste en agregar el carácter retuno por uno (¡los números de caracteres se suman entre sí !) Como en:

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

... pero eso sería realmente feo.

догонят
fuente
Todas son grandes ideas, pero convierten su código en código de solo escritura imposible de entender para su compañero de trabajo.
Ben
0

Puede hacer una impresión a cualquier cosa que sea un OutputStream con un PrintStream. De alguna manera como esta, imprimiendo en una secuencia de cadena:

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();

La secuencia de cadena se puede crear así: ByteArrayOutputStream:

ByteArrayOutputStream baos = new ByteArrayOutputStream();
armagedescu
fuente