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

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

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


当前回答

基于这个答案,我创建了MapBuilder类:

public class MapBuilder {

    public static Map<String, Object> build(Object... data) {
        Map<String, Object> result = new LinkedHashMap<>();

        if (data.length % 2 != 0) {
            throw new IllegalArgumentException("Odd number of arguments");
        }

        String key = null;
        Integer step = -1;

        for (Object value : data) {
            step++;
            switch (step % 2) {
                case 0:
                    if (value == null) {
                        throw new IllegalArgumentException("Null key value");
                    }
                    key = (String) value;
                    continue;
                case 1:
                    result.put(key, value);
                    break;
            }
        }

        return result;
    }

}

然后我创建类StringFormat用于字符串格式化:

public final class StringFormat {

    public static String format(String format, Object... args) {
        Map<String, Object> values = MapBuilder.build(args);

        for (Map.Entry<String, Object> entry : values.entrySet()) {
            String key = entry.getKey();
            Object value = entry.getValue();
            format = format.replace("$" + key, value.toString());
        }

        return format;
    }

}

你可以这样用:

String bookingDate = StringFormat.format("From $startDate to $endDate"), 
        "$startDate", formattedStartDate, 
        "$endDate", formattedEndDate
);

其他回答

您应该看看官方的ICU4J库。它提供了一个类似于JDK的MessageFormat类,但前者支持命名占位符。

与本页提供的其他解决方案不同。ICU4j是ICU项目的一部分,由IBM维护并定期更新。此外,它还支持高级用例,如多元化等。

下面是一个代码示例:

MessageFormat messageFormat =
        new MessageFormat("Publication written by {author}.");

Map<String, String> args = Map.of("author", "John Doe");

System.out.println(messageFormat.format(args));

我的答案是:

a)尽可能使用StringBuilder

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

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

我最终得到了下一个解决方案: 使用substitute()方法创建类templatessubstitute,并使用它格式化输出 然后创建一个字符串模板,并用值填充它

import java.util.*;
public class MyClass {

    public static void main(String args[]) {
    String template = "WRR = {WRR}, SRR = {SRR}\n" +
                      "char_F1 = {char_F1}, word_F1 = {word_F1}\n";
    
    Map<String, Object> values = new HashMap<>();
    values.put("WRR", 99.9);
    values.put("SRR", 99.8);
    values.put("char_F1", 80);
    values.put("word_F1", 70);
    
    String message = TemplateSubstitutor.substitute(values, template);
    
    System.out.println(message);
    }
}

class TemplateSubstitutor {
    public static String substitute(Map<String, Object> map, String input_str) {
        String output_str = input_str;
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            String key = entry.getKey();
            Object value = entry.getValue();
            output_str = output_str.replace("{" + key + "}", String.valueOf(value));
        }
        return output_str;
    }
    
}

我是一个小型库的作者,它可以做你想要的:

Student student = new Student("Andrei", 30, "Male");

String studStr = template("#{id}\tName: #{st.getName}, Age: #{st.getAge}, Gender: #{st.getGender}")
                    .arg("id", 10)
                    .arg("st", student)
                    .format();
System.out.println(studStr);

或者你可以串起参数:

String result = template("#{x} + #{y} = #{z}")
                    .args("x", 5, "y", 10, "z", 15)
                    .format();
System.out.println(result);

// Output: "5 + 10 = 15"

有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。