11

使用给出的示例java.util.Formattable(修改为在构造函数中实际设置值),事情似乎大部分正常工作:

import java.nio.CharBuffer;
import java.util.Formatter;
import java.util.Formattable;
import java.util.Locale;
import static java.util.FormattableFlags.*;

public class StockName implements Formattable {
    private String symbol, companyName, frenchCompanyName;
    public StockName(String symbol, String companyName,
                     String frenchCompanyName) {
        this.symbol = symbol;
        this.companyName = companyName;
        this.frenchCompanyName = frenchCompanyName;
    }

    public void formatTo(Formatter fmt, int f, int width, int precision) {
        StringBuilder sb = new StringBuilder();

        // decide form of name
        String name = companyName;
        if (fmt.locale().equals(Locale.FRANCE))
            name = frenchCompanyName;
        boolean alternate = (f & ALTERNATE) == ALTERNATE;
        boolean usesymbol = alternate || (precision != -1 && precision < 10);
        String out = (usesymbol ? symbol : name);

        // apply precision
        if (precision == -1 || out.length() < precision) {
            // write it all
            sb.append(out);
        } else {
            sb.append(out.substring(0, precision - 1)).append('*');
        }

        // apply width and justification
        int len = sb.length();
        if (len < width)
            for (int i = 0; i < width - len; i++)
                if ((f & LEFT_JUSTIFY) == LEFT_JUSTIFY)
                    sb.append(' ');
                else
                    sb.insert(0, ' ');

        fmt.format(sb.toString());
    }

    public String toString() {
        return String.format("%s - %s", symbol, companyName);
    }
}

跑步

System.out.printf("%s", new StockName("HUGE", "Huge Fruit, Inc.", "Fruit Titanesque, Inc."));

Huge Fruit, Inc.按预期打印。

但是,以下方法不起作用:

System.out.printf("%s", new StockName("PERC", "%Company, Inc.", "Fruit Titanesque, Inc."));

它抛出一个java.util.MissingFormatArgumentException

Exception in thread "main" java.util.MissingFormatArgumentException: Format specifier '%C'
        at java.util.Formatter.format(Formatter.java:2519)
        at java.util.Formatter.format(Formatter.java:2455)
        at StockName.formatTo(FormattableTest.java:44)
        at java.util.Formatter$FormatSpecifier.printString(Formatter.java:2879)
        at java.util.Formatter$FormatSpecifier.print(Formatter.java:2763)
        at java.util.Formatter.format(Formatter.java:2520)
        at java.io.PrintStream.format(PrintStream.java:970)
        at java.io.PrintStream.printf(PrintStream.java:871)
        at FormattableTest.main(FormattableTest.java:55)

该示例用于Formatter.format添加文本,而format应该格式化格式字符串。当应该附加的文本包含百分比时,这会导致事情中断。

我应该如何处理这个formatTo 我应该手动写入格式化程序的 Appendable ( formatter.out().append(text),它可以以某种方式抛出IOException) 吗?我是否应该尝试转义格式字符串(类似formatter.format(text.replace("%","%%")),尽管这可能还不够)?我应该将一个简单的格式字符串传递给格式化程序(formatter.format("%s", text),但这似乎是多余的)?所有这些都应该起作用,但是语义上的正确方法是什么?

澄清一下,在这种假设情况下,给定的参数StockName是用户控制的,可以是任意的;我没有对它们的精确控制(我不能禁止输入%)。但是,我可以编辑StockName.formatTo.

4

2 回答 2

5

其实很简单。您仅在格式化期间转义百分比字符,而不是在原始属性中:

  // apply precision
  if (precision == -1 || out.length() < precision) {
    // write it all
    sb.append(out.replaceAll("%", "%%"));
  }
  else {
    sb.append(out.substring(0, precision - 1).replaceAll("%", "%%")).append('*');
  }

然后,如果你这样做:

StockName stockName = new StockName("HUGE", "%Huge Fruit, Inc.", "Fruit Titanesque, Inc.");
System.out.printf("%s%n", stockName);
System.out.printf("%s%n", stockName.toString());
System.out.printf("%#s%n", stockName);
System.out.printf("%-10.8s%n", stockName);
System.out.printf("%.12s%n", stockName);
System.out.printf(Locale.FRANCE, "%25s%n", stockName);

输出如下所示:

%Huge Fruit, Inc.
HUGE - %Huge Fruit, Inc.
HUGE
HUGE      
%Huge Fruit*
   Fruit Titanesque, Inc.
于 2017-03-15T13:56:17.073 回答
2

如果你想打印百分比符号%,你必须通过双写来转义它,例如

System.out.printf("%s", new StockName("PERC", "%%Company, Inc.", "Fruit Titanesque, Inc."));

这将打印%Company, Inc.

于 2017-03-13T00:47:18.467 回答