Odpowiednik Sprintf w Javie


284

Printf został dodany do Javy w wersji 1.5, ale nie mogę znaleźć sposobu, aby wysłać dane wyjściowe do łańcucha zamiast do pliku (co robi sprintf w C). Czy ktoś wie jak to zrobić?

Odpowiedzi:


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

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

Zobacz format i jego składnię


28

Ciągi są niezmiennymi typami. Nie możesz ich modyfikować, zwracaj tylko nowe wystąpienia ciągów.

Z tego powodu formatowanie za pomocą metody instancji nie ma większego sensu, ponieważ należałoby ją wywołać w następujący sposób:

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

Oryginalni autorzy Java (i autorzy .NET) zdecydowali, że metoda statyczna ma większy sens w tej sytuacji, ponieważ nie modyfikujesz obiektu docelowego, ale zamiast tego wywołujesz metodę formatowania i przekazujesz ciąg wejściowy.

Oto przykład, dlaczego format()miałby być głupi jako metoda instancji. W .NET (i prawdopodobnie w Javie) Replace()jest metodą instancji.

Możesz to zrobić:

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

Jednak nic się nie dzieje, ponieważ ciągi są niezmienne. Replace()próbuje zwrócić nowy ciąg, ale jest on przypisany do niczego.

Powoduje to wiele typowych błędów debiutantów, takich jak:

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

Ponownie nic się nie dzieje, zamiast tego musisz zrobić:

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

Jeśli zrozumiesz, że ciągi są niezmienne, ma to doskonały sens. Jeśli nie, to jesteś po prostu zdezorientowany. Właściwym miejscem Replace()byłoby, gdzie format()jest, jako statyczna metoda String:

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

Teraz nie ma wątpliwości, co się dzieje.

Prawdziwe pytanie brzmi: dlaczego autorzy tych ram zdecydowali, że jedna powinna być metodą instancji, a druga statyczna? Moim zdaniem oba są bardziej elegancko wyrażone jako metody statyczne.

Niezależnie od twojej opinii, prawda jest taka, że ​​masz mniejszą skłonność do popełniania błędów przy użyciu wersji statycznej, a kod jest łatwiejszy do zrozumienia (brak ukrytych błędów).

Oczywiście istnieją pewne metody, które są doskonałe jako metody instancji, weź String.Length ()

int length = "123".Length();

W tej sytuacji jest oczywiste, że nie próbujemy modyfikować „123”, po prostu go sprawdzamy i zwracamy jego długość. Jest to idealny kandydat na metodę instancji.

Moje proste zasady dotyczące metod instancji na obiektach niezmiennych:

  • Jeśli chcesz zwrócić nową instancję tego samego typu, użyj metody statycznej.
  • W przeciwnym razie użyj metody instancji.

4
Widzę, że masz pomysł, który sugerowałem, aby łańcuch formatu miał zostać zmodyfikowany. Nigdy nie zastanawiałem się nad możliwością, że ktoś może oczekiwać, że Łańcuch się zmieni, ponieważ jego niezmienność jest tak fundamentalna.
— erickson

4
Ponieważ ciąg formatu jest zwykle bardziej podobny do „Cena to% 4d”, a nie „% 4d”, nadal widzę duży potencjał do zamieszania. Co masz przeciwko metodom statycznym? :)
— FlySwat,

44
Ta odpowiedź wydaje się nie mieć nic wspólnego z pytaniem.
— Steve McLeod,

2
Odpowiedź nie jest nawet Java, wydaje się być bardziej odpowiednia dla .NET
— Photodeus,

3
-1. Odebrane b / c jest styczne. I niekoniecznie najlepszy styl na niezmienne. Ten styl jest bardziej szczegółowy niż proste wywołania metod, szczególnie w przypadku operacji łańcuchowych. I porzuca polimorfizm w czasie wykonywania, ponieważ nazywa metody statyczne, co jest znaczące. YMMV.
— Andrew Janke

3

Oba rozwiązania symulują printf, ale w inny sposób. Na przykład, aby przekonwertować wartość na ciąg szesnastkowy, masz 2 następujące rozwiązania:

  • z format()najbliżej 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;
    }
  • z replace(char oldchar , char newchar), nieco szybszym, ale dość ograniczonym:

        ...
        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)));
        ...
  • Istnieje trzecie rozwiązanie polegające na dodaniu char do retjednego po drugim (char to liczby, które się dodają !), Na przykład:

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

... ale to byłoby naprawdę brzydkie.


Wszystkie wspaniałe pomysły, ale zamieniają kod w kod tylko do zapisu, którego nie można zrozumieć dla współpracownika.
— Ben

0

Możesz zrobić printf do wszystkiego, co jest OutputStream z PrintStream. Jakoś tak, drukując do strumienia ciągów:

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

Strumień ciągu można utworzyć w następujący sposób ByteArrayOutputStream:

ByteArrayOutputStream baos = new ByteArrayOutputStream();
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.