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.