Sprintf-Äquivalent in Java

284

Printf wurde mit der Version 1.5 zu Java hinzugefügt, aber ich kann anscheinend nicht herausfinden, wie die Ausgabe an eine Zeichenfolge anstatt an eine Datei gesendet werden kann (was sprintf in C tut). Weiß jemand, wie man das macht?

Papierpferd
quelle

Antworten:

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

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

Siehe Format und seine Syntax

Eugene Yokota
quelle
28

Strings sind unveränderliche Typen. Sie können sie nicht ändern, sondern nur neue Zeichenfolgeninstanzen zurückgeben.

Aus diesem Grund ist die Formatierung mit einer Instanzmethode wenig sinnvoll, da sie wie folgt aufgerufen werden müsste:

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

Die ursprünglichen Java-Autoren (und .NET-Autoren) haben entschieden, dass eine statische Methode in dieser Situation sinnvoller ist, da Sie das Ziel nicht ändern, sondern stattdessen eine Formatmethode aufrufen und eine Eingabezeichenfolge übergeben.

Hier ist ein Beispiel dafür, warum format()dies als Instanzmethode dumm wäre. In .NET (und wahrscheinlich in Java) Replace()ist eine Instanzmethode.

Du kannst das:

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

Es passiert jedoch nichts, da Zeichenfolgen unveränderlich sind. Replace()versucht, eine neue Zeichenfolge zurückzugeben, die jedoch nichts zugewiesen ist.

Dies verursacht viele häufige Anfängerfehler wie:

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

Auch hier passiert nichts, stattdessen müssen Sie tun:

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

Wenn Sie verstehen, dass Zeichenfolgen unveränderlich sind, ist dies absolut sinnvoll. Wenn Sie dies nicht tun, sind Sie nur verwirrt. Der richtige Ort dafür Replace()wäre, wo format()ist, als statische Methode von String:

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

Jetzt gibt es keine Frage mehr, was los ist.

Die eigentliche Frage ist, warum die Autoren dieser Frameworks entschieden haben, dass eine Instanzmethode und die andere statisch sein soll. Meiner Meinung nach werden beide eleganter als statische Methoden ausgedrückt.

Unabhängig von Ihrer Meinung ist die Wahrheit, dass Sie mit der statischen Version weniger anfällig für Fehler sind und der Code leichter zu verstehen ist (No Hidden Gotchas).

Natürlich gibt es einige Methoden, die sich perfekt als Instanzmethoden eignen. Nehmen Sie String.Length ()

int length = "123".Length();

In dieser Situation ist es offensichtlich, dass wir nicht versuchen, "123" zu ändern, sondern es nur untersuchen und seine Länge zurückgeben. Dies ist ein perfekter Kandidat für eine Instanzmethode.

Meine einfachen Regeln für Instanzmethoden für unveränderliche Objekte:

  • Wenn Sie eine neue Instanz desselben Typs zurückgeben müssen, verwenden Sie eine statische Methode.
  • Verwenden Sie andernfalls eine Instanzmethode.
FlySwat
quelle
4
Ich sehe, dass Sie irgendwie auf die Idee gekommen sind, dass ich vorgeschlagen habe, die Formatzeichenfolge zu ändern. Ich habe nie über die Möglichkeit nachgedacht, dass jemand erwartet, dass sich ein String ändert, da seine Unveränderlichkeit so grundlegend ist.
Erickson
4
Da die Formatzeichenfolge normalerweise eher "Der Preis ist% 4d" und nicht "% 4d" ist, sehe ich immer noch viel Verwirrungspotential. Was haben Sie gegen statische Methoden? :)
FlySwat
44
Diese Antwort scheint nichts mit der Frage zu tun zu haben.
Steve McLeod
2
Die Antwort ist nicht einmal Java, scheint für .NET
Photodeus
3
-1. Downvoted b / c ist es tangential. Und nicht unbedingt der beste Stil für Unveränderliche. Dieser Stil ist ausführlicher als einfache Methodenaufrufe, insbesondere für verkettete Operationen. Und es gibt den Laufzeitpolymorphismus auf, weil es statische Methoden aufruft, was von Bedeutung ist. YMMV.
Andrew Janke
3

Beide Lösungen simulieren printf, jedoch auf unterschiedliche Weise. Um beispielsweise einen Wert in eine Hex-Zeichenfolge zu konvertieren, haben Sie die folgenden 2 Lösungen:

  • mit format(), am nächsten an 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;
    }
  • mit replace(char oldchar , char newchar), etwas schneller aber ziemlich begrenzt:

        ...
        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)));
        ...
  • Es gibt eine dritte Lösung, die darin besteht, das Zeichen retnacheinander zu addieren (Zeichen sind Zahlen, die sich gegenseitig addieren !), Wie in:

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

... aber das wäre wirklich hässlich.

догонят
quelle
Alles gute Ideen, aber verwandelt Ihren Code in schreibgeschützten Code, der für Ihren Kollegen nicht zu verstehen ist.
Ben
0

Sie können mit einem PrintStream alles drucken, was ein OutputStream ist. Irgendwie so, in einen String-Stream drucken:

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

Der String-Stream kann wie folgt erstellt werden: ByteArrayOutputStream:

ByteArrayOutputStream baos = new ByteArrayOutputStream();
armagedescu
quelle