308

Printf got added to Java with the 1.5 release but I can't seem to find how to send the output to a string rather than a file (which is what sprintf does in C). Does anyone know how to do this?

ojblass
  • 21,146
  • 22
  • 83
  • 132
paperhorse
  • 4,095
  • 2
  • 22
  • 12

5 Answers5

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

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

See format and its syntax

Dave Jarvis
  • 30,436
  • 41
  • 178
  • 315
Eugene Yokota
  • 94,654
  • 45
  • 215
  • 319
27

Strings are immutable types. You cannot modify them, only return new string instances.

Because of that, formatting with an instance method makes little sense, as it would have to be called like:

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

The original Java authors (and .NET authors) decided that a static method made more sense in this situation, as you are not modifying the target, but instead calling a format method and passing in an input string.

Here is an example of why format() would be dumb as an instance method. In .NET (and probably in Java), Replace() is an instance method.

You can do this:

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

However, nothing happens, because strings are immutable. Replace() tries to return a new string, but it is assigned to nothing.

This causes lots of common rookie mistakes like:

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

Again, nothing happens, instead you have to do:

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

Now, if you understand that strings are immutable, that makes perfect sense. If you don't, then you are just confused. The proper place for Replace() would be where format() is, as a static method of String:

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

Now there is no question as to what's going on.

The real question is, why did the authors of these frameworks decide that one should be an instance method, and the other static? In my opinion, both are more elegantly expressed as static methods.

Regardless of your opinion, the truth is that you are less prone to make a mistake using the static version, and the code is easier to understand (No Hidden Gotchas).

Of course there are some methods that are perfect as instance methods, take String.Length()

int length = "123".Length();

In this situation, it's obvious we are not trying to modify "123", we are just inspecting it, and returning its length. This is a perfect candidate for an instance method.

My simple rules for Instance Methods on Immutable Objects:

  • If you need to return a new instance of the same type, use a static method.
  • Otherwise, use an instance method.
erickson
  • 265,237
  • 58
  • 395
  • 493
FlySwat
  • 172,459
  • 74
  • 246
  • 311
  • 4
    I see that you somehow you got the idea I was suggesting the format string was to be modified. I never considered the possibility that someone might expect a String to change, since their immutability is so fundamental. – erickson Oct 13 '08 at 18:14
  • 4
    Seeing as the format string is usually more like "The Price is %4d", and not "%4d", I still see a lot of potential for confusion. What do you have against static methods? :) – FlySwat Oct 13 '08 at 18:20
  • 51
    This answer seems to have nothing to do with the question. – Steve McLeod Jun 06 '09 at 07:58
  • 3
    The answer isn't even Java, seems to be more relevant to .NET – Photodeus Nov 19 '09 at 06:35
  • 5
    -1. Downvoted b/c it's tangential. And not necessarily the best style for immutables. This style is more verbose than simple method calls, particularly for chained operations. And it gives up run-time polymorphism because it's calling static methods, which is significant. YMMV. – Andrew Janke Apr 21 '14 at 03:11
  • 3
    Following your logic. `"ABC".toLowerCase()` would be wrong as well. – glglgl Jul 07 '16 at 11:23
  • 2
    Explaining why a putative `String.Format` should be a static method rather than an instance method does *nothing* to answer the question "what is the equivalent of sprintf in Java?". (The answer is "`String.format`, which is a static method" btw.) – Martin Bonner supports Monica Mar 28 '17 at 14:46
  • 1
    This API has been added tentatively to Java 13. E.g., `String str = "%s: %d".formatted(name, val);` is now supported. This will be especially helpful for forming exception messages. – erickson Dec 20 '19 at 20:20
8

Since Java 13 you have formatted 1 method on String, which was added along with text blocks as a preview feature 2. You can use it instead of String.format()

Assertions.assertEquals(
   "%s %d %.3f".formatted("foo", 123, 7.89),
   "foo 123 7.890"
);
andreoss
  • 1,570
  • 1
  • 10
  • 25
2

Both solutions workto simulate printf, but in a different way. For instance, to convert a value to a hex string, you have the 2 following solutions:

  • with format(), closest to 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;
    }
    
  • with replace(char oldchar , char newchar), somewhat faster but pretty limited:

        ...
        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)));
        ...
    
  • There is a third solution consisting of just adding the char to ret one by one (char are numbers that add to each other!) such as in:

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

...but that'd be really ugly.

  • All great ideas, but turns your code into write-only code impossible to understand for your coworker. – Ben Jul 14 '17 at 09:01
2

You can do a printf with a PrintStream to anything that is an OutputStream. Somehow like this, printing into a string stream:

PrintStream ps = new PrintStream(baos);
ps.printf("there is a %s from %d %s", "hello", 3, "friends");
System.out.println(baos.toString());

This outputs following text there is a hello from 3 friends The string stream can be created like this ByteArrayOutputStream:

ByteArrayOutputStream baos = new ByteArrayOutputStream();

You can accumulate many formats:

PrintStream ps = new PrintStream(baos);
ps.printf("there is a %s from %d %s ", "hello", 3, "friends");
ps.printf("there are %d % from a %", 2, "kisses", "girl");
System.out.println(baos.toString());

This outputs there is a hello from 3 friends there are 2 kisses from a girl
Call reset on ByteArrayOutputStream to generate a clean new string

ps.printf("there is a %s from %d %s", "flip", 5, "haters");
baos.reset(); //need reset to write new string
ps.printf("there are %d % from a %", 2, "kisses", "girl");
System.out.println(baos.toString());

The output will be there are 2 kisses from a girl

armagedescu
  • 1,758
  • 2
  • 20
  • 31