【问题标题】:Named placeholders in string formatting字符串格式的命名占位符
【发布时间】:2010-02-18 06:06:09
【问题描述】:

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

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

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

【问题讨论】:

标签: java string-formatting


【解决方案1】:

jakarta commons lang 的 StrSubstitutor 是一种轻量级的方法,前提是您的值已经正确格式化。

http://commons.apache.org/proper/commons-lang/javadocs/api-3.1/org/apache/commons/lang3/text/StrSubstitutor.html

Map<String, String> values = new HashMap<String, String>();
values.put("value", x);
values.put("column", y);
StrSubstitutor sub = new StrSubstitutor(values, "%(", ")");
String result = sub.replace("There's an incorrect value '%(value)' in column # %(column)");

以上结果:

“第 2 列中的值 '1' 不正确”

使用 Maven 时,您可以将此依赖项添加到您的 pom.xml:

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.4</version>
</dependency>

【讨论】:

  • 我很失望,如果找不到键,库不会抛出,但是,如果您使用默认语法 (${arg}) 而不是上面的自定义语法 (%(arg)),那么正则表达式不会编译,这是想要的效果。
  • 您可以设置自定义 VariableResolver 并在地图中不存在密钥时抛出异常。
  • 旧线程,但从 3.6 开始,不推荐使用 text 包以支持 commons-text。 commons.apache.org/proper/commons-text
【解决方案2】:

不完全是,但您可以使用MessageFormat 多次引用一个值:

MessageFormat.format("There's an incorrect value \"{0}\" in column # {1}", x, y);

上面也可以用 String.format() 来完成,但是如果你需要构建复杂的表达式,我发现 messageFormat 语法更清晰,而且你不需要关心你放入字符串的对象的类型

【讨论】:

  • 不知道为什么不能,字符串中的位置并不重要,只有args列表中的位置,这使其成为重命名问题。您知道键的名称,这意味着您可以决定键在参数列表中的位置。从现在开始,值将被称为 0,列被称为 1: MessageeFormat.format("There's an wrong value \"{0}\" in column # {1}, using {0} as value can cause many questions", valueMap .get('value'), valueMap.get('column'));
  • 感谢您的提示,它帮助我编写了完全符合我要求的简单函数(我已将其放在下面)。
  • 同意,语法更简洁。太糟糕了 MessageFormat 在格式化数值时有自己的想法。
  • 而且它似乎忽略了单引号包围的占位符。
  • MessageFormat 很棒,但是对于相对较大的 json 内容来说很麻烦
【解决方案3】:

另一个简单命名占位符的 Apache Common StringSubstitutor 示例。

String template = "Welcome to {theWorld}. My name is {myName}.";

Map<String, String> values = new HashMap<>();
values.put("theWorld", "Stackoverflow");
values.put("myName", "Thanos");

String message = StringSubstitutor.replace(template, values, "{", "}");

System.out.println(message);

// Welcome to Stackoverflow. My name is Thanos.

【讨论】:

  • 如果你希望加载非常大的文件,我发现这个库还支持replaceIn,它将值替换到缓冲区中:StringBuilder 或 TextStringBuilder。使用这种方法,文件的全部内容不会被加载到内存中。
  • 无意冒犯。以替换的方式格式化字符串是非常不合理和不可读的。尤其是与其他现代程序语言相比。
【解决方案4】:

您可以使用StringTemplate 库,它提供您想要的以及更多。

import org.antlr.stringtemplate.*;

final StringTemplate hello = new StringTemplate("Hello, $name$");
hello.setAttribute("name", "World");
System.out.println(hello.toString());

【讨论】:

  • ' 字符有问题:unexpected char: '''
【解决方案5】:

感谢您的所有帮助!使用你所有的线索,我编写了例程来做我想要的——使用字典进行类似 python 的字符串格式化。由于我是 Java 新手,任何提示都非常感谢。

public static String dictFormat(String format, Hashtable<String, Object> values) {
    StringBuilder convFormat = new StringBuilder(format);
    Enumeration<String> keys = values.keys();
    ArrayList valueList = new ArrayList();
    int currentPos = 1;
    while (keys.hasMoreElements()) {
        String key = keys.nextElement(),
        formatKey = "%(" + key + ")",
        formatPos = "%" + Integer.toString(currentPos) + "$";
        int index = -1;
        while ((index = convFormat.indexOf(formatKey, index)) != -1) {
            convFormat.replace(index, index + formatKey.length(), formatPos);
            index += formatPos.length();
        }
        valueList.add(values.get(key));
        ++currentPos;
    }
    return String.format(convFormat.toString(), valueList.toArray());
}

【讨论】:

  • 与 Lombo 的回答不同,这不会陷入无限循环,因为 formatPos 不能包含 formatKey
  • 警告: 因为循环将中间替换结果视为它们自己的格式字符串,this solution is vulnerable to format string attacks。任何正确的解决方案都应该通过格式字符串一次。
【解决方案6】:
public static String format(String format, Map<String, Object> values) {
    StringBuilder formatter = new StringBuilder(format);
    List<Object> valueList = new ArrayList<Object>();

    Matcher matcher = Pattern.compile("\\$\\{(\\w+)}").matcher(format);

    while (matcher.find()) {
        String key = matcher.group(1);

        String formatKey = String.format("${%s}", key);
        int index = formatter.indexOf(formatKey);

        if (index != -1) {
            formatter.replace(index, index + formatKey.length(), "%s");
            valueList.add(values.get(key));
        }
    }

    return String.format(formatter.toString(), valueList.toArray());
}

例子:

String format = "My name is ${1}. ${0} ${1}.";

Map<String, Object> values = new HashMap<String, Object>();
values.put("0", "James");
values.put("1", "Bond");

System.out.println(format(format, values)); // My name is Bond. James Bond.

【讨论】:

  • 这应该是答案,因为它避免了这里大多数其他解决方案容易受到的格式字符串攻击。请注意,Java 9 使它更简单,支持.replaceAll() string substitution callbacks
  • 这应该是答案,因为它不使用任何外部库。
【解决方案7】:

这是一个旧线程,但为了记录,您也可以使用 Java 8 样式,如下所示:

public static String replaceParams(Map<String, String> hashMap, String template) {
    return hashMap.entrySet().stream().reduce(template, (s, e) -> s.replace("%(" + e.getKey() + ")", e.getValue()),
            (s, s2) -> s);
}

用法:

public static void main(String[] args) {
    final HashMap<String, String> hashMap = new HashMap<String, String>() {
        {
            put("foo", "foo1");
            put("bar", "bar1");
            put("car", "BMW");
            put("truck", "MAN");
        }
    };
    String res = replaceParams(hashMap, "This is '%(foo)' and '%(foo)', but also '%(bar)' '%(bar)' indeed.");
    System.out.println(res);
    System.out.println(replaceParams(hashMap, "This is '%(car)' and '%(foo)', but also '%(bar)' '%(bar)' indeed."));
    System.out.println(replaceParams(hashMap, "This is '%(car)' and '%(truck)', but also '%(foo)' '%(bar)' + '%(truck)' indeed."));
}

输出将是:

This is 'foo1' and 'foo1', but also 'bar1' 'bar1' indeed.
This is 'BMW' and 'foo1', but also 'bar1' 'bar1' indeed.
This is 'BMW' and 'MAN', but also 'foo1' 'bar1' + 'MAN' indeed.

【讨论】:

  • 这很棒,但遗憾的是它违反了这里的规范docs.oracle.com/javase/8/docs/api/java/util/stream/… 如果第一个参数是标识,则组合器函数必须返回第二个参数。上面的那个会返回身份。它也违反了这条规则:combiner.apply(u, accumulator.apply(identity, t)) == accumulator.apply(u, t)
  • 有趣...但前提是您提出了一种更好的方法来传递地图,如果可能的话,也可以像大多数格式化代码一样在模板之后。
  • 警告: 因为.reduce() 将中间替换结果视为它们自己的格式字符串,this solution is vulnerable to format string attacks。任何正确的解决方案都应该通过格式字符串一次。
【解决方案8】:

我是a small library 的作者,这正是你想要的:

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"

【讨论】:

  • 是否可以使用您的库进行基于条件的格式化?
  • @gaurav 不完全是。如果您需要,您需要一个功能齐全的模板库。
【解决方案9】:

可以使用Apache Commons StringSubstitutor。请注意,StrSubstitutor 已弃用。

import org.apache.commons.text.StringSubstitutor;
// ...
Map<String, String> values = new HashMap<>();
values.put("animal", "quick brown fox");
values.put("target", "lazy dog");
StringSubstitutor sub = new StringSubstitutor(values);
String result = sub.replace("The ${animal} jumped over the ${target}.");
// "The quick brown fox jumped over the lazy dog."

该类支持为变量提供默认值。

String result = sub.replace("The number is ${undefined.property:-42}.");
// "The number is 42."

要使用递归变量替换,请调用setEnableSubstitutionInVariables(true);

Map<String, String> values = new HashMap<>();
values.put("b", "c");
values.put("ac", "Test");
StringSubstitutor sub = new StringSubstitutor(values);
sub.setEnableSubstitutionInVariables(true);
String result = sub.replace("${a${b}}");
// "Test"

【讨论】:

    【解决方案10】:

    Apache Commons Lang 的replaceEach 方法可能会派上用场,具体取决于您的具体需求。您可以通过这个单一的方法调用轻松地使用它来按名称替换占位符:

    StringUtils.replaceEach("There's an incorrect value '%(value)' in column # %(column)",
                new String[] { "%(value)", "%(column)" }, new String[] { x, y });
    

    给定一些输入文本,这会将第一个字符串数组中出现的所有占位符替换为第二个字符串数组中的相应值。

    【讨论】:

      【解决方案11】:

      在写这篇文章的那一刻,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();
          }
      }
      

      【讨论】:

        【解决方案12】:

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

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

        这是一个代码示例:

        MessageFormat messageFormat =
                new MessageFormat("Publication written by {author}.");
        
        Map<String, String> args = Map.of("author", "John Doe");
        
        System.out.println(messageFormat.format(args));
        

        【讨论】:

          【解决方案13】:

          你可以在一个字符串助手类上有这样的东西

          /**
           * An interpreter for strings with named placeholders.
           *
           * For example given the string "hello %(myName)" and the map <code>
           *      <p>Map<String, Object> map = new HashMap<String, Object>();</p>
           *      <p>map.put("myName", "world");</p>
           * </code>
           *
           * the call {@code format("hello %(myName)", map)} returns "hello world"
           *
           * It replaces every occurrence of a named placeholder with its given value
           * in the map. If there is a named place holder which is not found in the
           * map then the string will retain that placeholder. Likewise, if there is
           * an entry in the map that does not have its respective placeholder, it is
           * ignored.
           *
           * @param str
           *            string to format
           * @param values
           *            to replace
           * @return formatted string
           */
          public static String format(String str, Map<String, Object> values) {
          
              StringBuilder builder = new StringBuilder(str);
          
              for (Entry<String, Object> entry : values.entrySet()) {
          
                  int start;
                  String pattern = "%(" + entry.getKey() + ")";
                  String value = entry.getValue().toString();
          
                  // Replace every occurence of %(key) with value
                  while ((start = builder.indexOf(pattern)) != -1) {
                      builder.replace(start, start + pattern.length(), value);
                  }
              }
          
              return builder.toString();
          }
          

          【讨论】:

          • 非常感谢,它几乎可以满足我的需求,但唯一的问题是它没有考虑修饰符(考虑“%(key)08d”)
          • 还要注意,如果使用的任何值包含相应的条目,则会进入无限循环。
          • 警告: 因为循环将中间替换结果视为它们自己的格式字符串,this solution is vulnerable to format string attacks。任何正确的解决方案都应该通过格式字符串一次。
          【解决方案14】:

          基于answer我创建了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
          );
          

          【讨论】:

          【解决方案15】:

          我还创建了一个 util/helper 类(使用 jdk 8),它可以格式化字符串并替换出现的变量。

          为此,我使用了 Matchers 的“appendReplacement”方法,该方法仅在格式字符串的受影响部分上进行所有替换和循环。

          辅助类目前没有很好的 javadoc 文档记录。我将来会改变这个;) 无论如何,我评论了最重要的几行(我希望如此)。

              public class FormatHelper {
          
              //Prefix and suffix for the enclosing variable name in the format string.
              //Replace the default values with any you need.
              public static final String DEFAULT_PREFIX = "${";
              public static final String DEFAULT_SUFFIX = "}";
          
              //Define dynamic function what happens if a key is not found.
              //Replace the defualt exception with any "unchecked" exception type you need or any other behavior.
              public static final BiFunction<String, String, String> DEFAULT_NO_KEY_FUNCTION =
                      (fullMatch, variableName) -> {
                          throw new RuntimeException(String.format("Key: %s for variable %s not found.",
                                                                   variableName,
                                                                   fullMatch));
                      };
              private final Pattern variablePattern;
              private final Map<String, String> values;
              private final BiFunction<String, String, String> noKeyFunction;
              private final String prefix;
              private final String suffix;
          
              public FormatHelper(Map<String, String> values) {
                  this(DEFAULT_NO_KEY_FUNCTION, values);
              }
          
              public FormatHelper(
                      BiFunction<String, String, String> noKeyFunction, Map<String, String> values) {
                  this(DEFAULT_PREFIX, DEFAULT_SUFFIX, noKeyFunction, values);
              }
          
              public FormatHelper(String prefix, String suffix, Map<String, String> values) {
                  this(prefix, suffix, DEFAULT_NO_KEY_FUNCTION, values);
              }
          
              public FormatHelper(
                      String prefix,
                      String suffix,
                      BiFunction<String, String, String> noKeyFunction,
                      Map<String, String> values) {
                  this.prefix = prefix;
                  this.suffix = suffix;
                  this.values = values;
                  this.noKeyFunction = noKeyFunction;
          
                  //Create the Pattern and quote the prefix and suffix so that the regex don't interpret special chars.
                  //The variable name is a "\w+" in an extra capture group.
                  variablePattern = Pattern.compile(Pattern.quote(prefix) + "(\\w+)" + Pattern.quote(suffix));
              }
          
              public static String format(CharSequence format, Map<String, String> values) {
                  return new FormatHelper(values).format(format);
              }
          
              public static String format(
                      CharSequence format,
                      BiFunction<String, String, String> noKeyFunction,
                      Map<String, String> values) {
                  return new FormatHelper(noKeyFunction, values).format(format);
              }
          
              public static String format(
                      String prefix, String suffix, CharSequence format, Map<String, String> values) {
                  return new FormatHelper(prefix, suffix, values).format(format);
              }
          
              public static String format(
                      String prefix,
                      String suffix,
                      BiFunction<String, String, String> noKeyFunction,
                      CharSequence format,
                      Map<String, String> values) {
                  return new FormatHelper(prefix, suffix, noKeyFunction, values).format(format);
              }
          
              public String format(CharSequence format) {
          
                  //Create matcher based on the init pattern for variable names.
                  Matcher matcher = variablePattern.matcher(format);
          
                  //This buffer will hold all parts of the formatted finished string.
                  StringBuffer formatBuffer = new StringBuffer();
          
                  //loop while the matcher finds another variable (prefix -> name <- suffix) match
                  while (matcher.find()) {
          
                      //The root capture group with the full match e.g ${variableName}
                      String fullMatch = matcher.group();
          
                      //The capture group for the variable name resulting from "(\w+)" e.g. variableName
                      String variableName = matcher.group(1);
          
                      //Get the value in our Map so the Key is the used variable name in our "format" string. The associated value will replace the variable.
                      //If key is missing (absent) call the noKeyFunction with parameters "fullMatch" and "variableName" else return the value.
                      String value = values.computeIfAbsent(variableName, key -> noKeyFunction.apply(fullMatch, key));
          
                      //Escape the Map value because the "appendReplacement" method interprets the $ and \ as special chars.
                      String escapedValue = Matcher.quoteReplacement(value);
          
                      //The "appendReplacement" method replaces the current "full" match (e.g. ${variableName}) with the value from the "values" Map.
                      //The replaced part of the "format" string is appended to the StringBuffer "formatBuffer".
                      matcher.appendReplacement(formatBuffer, escapedValue);
                  }
          
                  //The "appendTail" method appends the last part of the "format" String which has no regex match.
                  //That means if e.g. our "format" string has no matches the whole untouched "format" string is appended to the StringBuffer "formatBuffer".
                  //Further more the method return the buffer.
                  return matcher.appendTail(formatBuffer)
                                .toString();
              }
          
              public String getPrefix() {
                  return prefix;
              }
          
              public String getSuffix() {
                  return suffix;
              }
          
              public Map<String, String> getValues() {
                  return values;
              }
          }
          

          您可以使用值(或后缀前缀或 noKeyFunction)为特定 Map 创建类实例 喜欢:

              Map<String, String> values = new HashMap<>();
              values.put("firstName", "Peter");
              values.put("lastName", "Parker");
          
          
              FormatHelper formatHelper = new FormatHelper(values);
              formatHelper.format("${firstName} ${lastName} is Spiderman!");
              // Result: "Peter Parker is Spiderman!"
              // Next format:
              formatHelper.format("Does ${firstName} ${lastName} works as photographer?");
              //Result: "Does Peter Parker works as photographer?"
          

          此外,您可以定义如果缺少值 Map 中的键会发生什么(以两种方式起作用,例如格式字符串中的错误变量名或 Map 中缺少键)。 默认行为是抛出未经检查的异常(未经检查,因为我使用无法处理检查异常的默认 jdk8 函数),例如:

              Map<String, String> map = new HashMap<>();
              map.put("firstName", "Peter");
              map.put("lastName", "Parker");
          
          
              FormatHelper formatHelper = new FormatHelper(map);
              formatHelper.format("${missingName} ${lastName} is Spiderman!");
              //Result: RuntimeException: Key: missingName for variable ${missingName} not found.
          

          您可以在构造函数调用中定义自定义行为,例如:

          Map<String, String> values = new HashMap<>();
          values.put("firstName", "Peter");
          values.put("lastName", "Parker");
          
          
          FormatHelper formatHelper = new FormatHelper(fullMatch, variableName) -> variableName.equals("missingName") ? "John": "SOMETHING_WRONG", values);
          formatHelper.format("${missingName} ${lastName} is Spiderman!");
          // Result: "John Parker is Spiderman!"
          

          或将其委托回默认的无键行为:

          ...
              FormatHelper formatHelper = new FormatHelper((fullMatch, variableName) ->   variableName.equals("missingName") ? "John" :
                      FormatHelper.DEFAULT_NO_KEY_FUNCTION.apply(fullMatch,
                                                                 variableName), map);
          ...
          

          为了更好地处理,还有静态方法表示,例如:

          Map<String, String> values = new HashMap<>();
          values.put("firstName", "Peter");
          values.put("lastName", "Parker");
          
          FormatHelper.format("${firstName} ${lastName} is Spiderman!", map);
          // Result: "Peter Parker is Spiderman!"
          

          【讨论】:

            【解决方案16】:

            有 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;
            }
            

            有关更多信息,请阅读项目自述文件。

            【讨论】:

              【解决方案17】:

              我的回答是:

              a) 尽可能使用 StringBuilder

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

              使用外部库似乎有点矫枉过正,我相信当 StringBuilder 在内部转换为 String 时会显着降低性能。

              【讨论】:

                【解决方案18】:

                试试Freemarker,模板库。

                【讨论】:

                • Freemarker?我想他愿意知道,如何用普通的 java 做到这一点。无论如何,如果 Freemarker 是可能的答案,那么我可以说 JSP 也将是正确的答案吗?
                • 谢谢,但对于我手头的任务,这似乎有点过头了。不过谢谢。
                • @Rakesh JSP 是一个非常“视图/FE”特定的东西。我过去曾使用 FreeMarker 生成 XML,有时甚至生成 JAVA 文件。安迪恐怕你必须自己编写一个实用程序(或像上面规定的那样)
                • @Boris freemarker vs velocity vs stringtemplate 哪个更好?
                【解决方案19】:

                我很快就试过了

                public static void main(String[] args) 
                {
                    String rowString = "replace the value ${var1} with ${var2}";
                    
                    Map<String,String> mappedValues = new HashMap<>();
                    
                    mappedValues.put("var1", "Value 1");
                    mappedValues.put("var2", "Value 2");
                    
                    System.out.println(replaceOccurence(rowString, mappedValues));
                }
                
                private static  String replaceOccurence(String baseStr ,Map<String,String> mappedValues)
                {
                    for(String key :mappedValues.keySet())
                    {
                        baseStr = baseStr.replace("${"+key+"}", mappedValues.get(key));
                    }
                    
                    return baseStr;
                }
                

                【讨论】:

                  【解决方案20】:

                  不幸的是,快速回答是否定的。但是,您可以非常接近合理的语法:

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

                  至少比String.format 更具可读性和可预测性!

                  【讨论】:

                    【解决方案21】:

                    https://dzone.com/articles/java-string-format-examples String.format(inputString, [listOfParams]) 将是最简单的方法。字符串中的占位符可以按顺序定义。有关更多详细信息,请查看提供的链接。

                    【讨论】:

                      猜你喜欢
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2022-11-18
                      • 2013-08-13
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      相关资源
                      最近更新 更多