在Python中,格式化字符串时,我可以按名称而不是按位置填充占位符,如下所示:

print "There's an incorrect value '%(value)s' in column # %(column)d" % \
  { 'value': x, 'column': y }

我想知道这在Java中是否可能(希望没有外部库)?


当前回答

不幸的是,答案是否定的。然而,你可以非常接近一个合理的语法:

"""
   You are $compliment!
"""
.replace('$compliment', 'awesome');

它比String更具可读性和可预测性。至少是格式!

其他回答

在编写本文时,Java中还没有内置任何东西。我建议编写自己的实现。我的偏好是一个简单流畅的构建器接口,而不是创建一个映射并将其传递给函数——你最终会得到一个漂亮的连续代码块,例如:

String result = new TemplatedStringBuilder("My name is {{name}} and I from {{town}}")
   .replace("name", "John Doe")
   .replace("town", "Sydney")
   .finish();

下面是一个简单的实现:

class TemplatedStringBuilder {

    private final static String TEMPLATE_START_TOKEN = "{{";
    private final static String TEMPLATE_CLOSE_TOKEN = "}}";

    private final String template;
    private final Map<String, String> parameters = new HashMap<>();

    public TemplatedStringBuilder(String template) {
        if (template == null) throw new NullPointerException();
        this.template = template;
    }

    public TemplatedStringBuilder replace(String key, String value){
        parameters.put(key, value);
        return this;
    }

    public String finish(){

        StringBuilder result = new StringBuilder();

        int startIndex = 0;

        while (startIndex < template.length()){

            int openIndex  = template.indexOf(TEMPLATE_START_TOKEN, startIndex);

            if (openIndex < 0){
                result.append(template.substring(startIndex));
                break;
            }

            int closeIndex = template.indexOf(TEMPLATE_CLOSE_TOKEN, openIndex);

            if(closeIndex < 0){
                result.append(template.substring(startIndex));
                break;
            }

            String key = template.substring(openIndex + TEMPLATE_START_TOKEN.length(), closeIndex);

            if (!parameters.containsKey(key)) throw new RuntimeException("missing value for key: " + key);

            result.append(template.substring(startIndex, openIndex));
            result.append(parameters.get(key));

            startIndex = closeIndex + TEMPLATE_CLOSE_TOKEN.length();
        }

        return result.toString();
    }
}

截至2022年,最新的解决方案是Apache Commons Text StringSubstitutor

医生说:

// Build map
 Map<String, String> valuesMap = new HashMap<>();
 valuesMap.put("animal", "quick brown fox");
 valuesMap.put("target", "lazy dog");
 String templateString = "The ${animal} jumped over the ${target} ${undefined.number:-1234567890} times.";

 // Build StringSubstitutor
 StringSubstitutor sub = new StringSubstitutor(valuesMap);

 // Replace
 String resolvedString = sub.replace(templateString)

;

我的答案是:

a)尽可能使用StringBuilder

b)保持“占位符”的位置(以任何形式:整数是最好的,特殊字符如dollar宏等),然后使用StringBuilder.insert()(参数的几个版本)。

当StringBuilder内部转换为String时,使用外部库似乎有些过度,而且我认为会显著降低性能。

有Java插件使用字符串插值在Java(像在Kotlin, JavaScript)。支持Java 8,9,10,11…https://github.com/antkorwin/better-strings

在字符串字面量中使用变量:

int a = 3;
int b = 4;
System.out.println("${a} + ${b} = ${a+b}");

使用表达式:

int a = 3;
int b = 4;
System.out.println("pow = ${a * a}");
System.out.println("flag = ${a > b ? true : false}");

使用功能:

@Test
void functionCall() {
    System.out.println("fact(5) = ${factorial(5)}");
}

long factorial(int n) {
    long fact = 1;
    for (int i = 2; i <= n; i++) {
        fact = fact * i;
    }
    return fact;
}

更多信息,请阅读项目README。

试试Freemarker,模板库。