相当于Java中的Sprintf

Printf在1.5版本中添加到Java,但我似乎找不到如何将输出发送到字符串而不是文件(这是sprintf在C中所做的)。有人知道如何做到这一点吗?

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


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

参见格式及其语法

字符串是不可变类型。你不能修改它们,只能返回新的字符串实例。

正因为如此,使用实例方法格式化没有什么意义,因为它必须像这样调用:

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

最初的Java作者(和。net作者)认为静态方法在这种情况下更有意义,因为您不需要修改目标,而是调用一个格式方法并传入一个输入字符串。

下面是一个例子,说明为什么format()作为实例方法是哑的。在. net中(可能在Java中),Replace()是一个实例方法。

你可以这样做:

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

然而,什么也不会发生,因为字符串是不可变的。Replace()尝试返回一个新字符串,但它没有被赋值给任何东西。

这会导致很多常见的新手错误,比如:

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

同样,什么也不会发生,相反,你必须做:

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

现在,如果你明白字符串是不可变的,这就说得通了。如果你没有,那么你只是困惑。Replace()的正确位置应该是format()所在的位置,作为String的静态方法:

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

现在发生了什么是毫无疑问的。

真正的问题是,为什么这些框架的作者决定一个应该是实例方法,而另一个应该是静态方法?在我看来,这两种方法都可以更优雅地表示为静态方法。

不管您的观点如何,事实是使用静态版本更不容易犯错误,并且代码更容易理解(没有隐藏的陷阱)。

当然,有一些方法作为实例方法是完美的,比如string。length ()

int length = "123".Length();

在这种情况下,很明显我们并没有试图修改“123”,我们只是检查它,并返回它的长度。这是实例方法的理想候选。

我对不可变对象的实例方法的简单规则:

  • 如果需要返回相同类型的新实例,请使用静态方法。
  • 否则,使用实例方法。
这两种解决方案都可以模拟printf,但方式不同。 例如,要将一个值转换为十六进制字符串,您有以下两个解决方案:

  • format(),最接近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.

你可以用PrintStream对任何输出流执行printf。 就像这样,打印到字符串流:

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

这输出以下文本有来自3个朋友的hello 字符串流可以像这样创建ByteArrayOutputStream:

ByteArrayOutputStream baos = new ByteArrayOutputStream();

你可以积累很多格式:

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

输出有三个朋友的问候,一个女孩的两个吻
调用ByteArrayOutputStream上的重置来生成一个干净的新字符串

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

输出将是一个女孩给了我两个吻

自Java 13以来,你在String上有formatted 1方法,它与文本块一起添加为预览功能2。 你可以用它来代替String.format()

Assertions.assertEquals(
"%s %d %.3f".formatted("foo", 123, 7.89),
"foo 123 7.890"
);