【问题标题】:Is there a way to render a string like 'Hello, %(name)s' % {'name':'Felix'} in Java?有没有办法在 Java 中呈现像 'Hello, %(name)s' % {'name':'Felix'} 这样的字符串?
【发布时间】:2013-07-26 04:20:29
【问题描述】:

在 Python 中我们可以轻松做到这一点:

data = {'name':'Felix'}
s = 'Hello, %(name)s' % data
s
'Hello, Felix'

在 Java 中是否有类似的方式来实现相同的东西?

PS: 对不起,不清楚的问题。用例是:我们有一个存储键值的映射,模板只需要在映射中指定一个键,那么键的值就会在模板中键所在的位置。

【问题讨论】:

    标签: java python render


    【解决方案1】:

    在这个用例下,您需要像velocity或freemarker这样的模板引擎来使用类似Map的数据结构来呈现字符串模板,java中没有内置模块可以做到这一点。像这样(有速度):

    public static void main(String[] args) {
        Context context = new VelocityContext();
        context.put("appid", "9876543d1");
        context.put("ds", "2013-09-11");
        StringWriter sw = new StringWriter();
        String template = "APPID is ${appid} and DS is ${ds}";
        Velocity.evaluate(context, sw, "velocity", template);
        System.out.println(sw.toString());
    }
    

    【讨论】:

      【解决方案2】:

      如果你只想用字符串替换字符串,那么我答案第二部分的代码会更好

      Java Formatter 类不支持 %(key)s 形式,但您可以使用 %index$s,其中索引从 1 开始计数,如本例所示

      System.out.format("%3$s, %2$s, %1s", "a", "b", "c");
      // indexes                            1    2    3
      

      输出:

      c, b, a
      

      所以你需要做的就是创建一些包含模式中使用的值的数组,并将键名更改为其对应的索引(增加 1,因为 Formatter 使用的第一个索引写为1$ 而不是0$ 之类的我们期望数组索引)。

      这里是为你做的方法示例

      // I made this Pattern static and put it outside of method to compile it only once,
      // also it will match every (xxx) that has % before it, but wont include %
      static Pattern formatPattern = Pattern.compile("(?<=%)\\(([^)]+)\\)");
      
      public static String format(String pattern, Map<String, ?> map) {
          StringBuffer sb = new StringBuffer();
          List<Object> valuesList = new ArrayList<>();
      
          Matcher m = formatPattern.matcher(pattern);
      
          while (m.find()) {
      
              String key = m.group(1);//group 1 contains part inside parenthesis
      
              Object value = map.get(key);
              // If map doesn't contain key, value will be null.
              // If you want to react somehow to null value like throw some
              // Exception
              // now is the good time.
      
              if (valuesList.contains(value)) {
                  m.appendReplacement(sb, (valuesList.indexOf(value) + 1) + "\\$");
              } else {
                  valuesList.add(value);
                  m.appendReplacement(sb, valuesList.size() + "\\$");
              }
          }
          m.appendTail(sb);
      
          return String.format(sb.toString(), valuesList.toArray());
      }
      

      用法

      Map<String, Object> map = new HashMap<>();
      map.put("name", "Felix");
      map.put("age", 70);
      
      String myPattern = 
              "Hi %(emptyKey)s! My name is %(name)s %(name)s and I am %(age)s years old";
      System.out.println(format(myPattern, map));
      

      输出:

      Hi null! My name is Felix Felix and I am 70 years old
      

      如您所见,您可以多次使用相同的键(在我们的例子中为 name),如果您的地图不包含字符串模式中使用的键(如 emptyKey),它将被替换为 null


      以上版本旨在让您设置数据类型,如s d 等,但如果您的数据将始终替换为字符串,那么您可以跳过String.format(sb.toString(), valuesList.toArray()) 并将所有键替换为值早一点。

      这是一个更简单的版本,它只接受具有 &lt;String,String&gt; 键值关系的映射。

      static Pattern stringsPattern = Pattern.compile("%\\(([^)]+)\\)s\\b");
      
      public static String formatStrings(String pattern, Map<String, String> map) {
          StringBuffer sb = new StringBuffer();
          Matcher m = stringsPattern.matcher(pattern);
      
          while (m.find()) {
              // we can't use null as replacement so we need to convert it to String
              // first. We can do it with String.valueOf method
              m.appendReplacement(sb, String.valueOf(map.get(m.group(1))));
          }
          m.appendTail(sb);
      
          return sb.toString();
      }
      

      【讨论】:

        【解决方案3】:

        如果您想要更高级的技术,例如 i18n 支持,您可以使用高级消息格式功能

        例如: 在语言属性文件中添加属性“模板”,这是您的消息

        template = At {2,time,short} on {2,date,long}, \
            we detected {1,number,integer} spaceships on \
            the planet {0}.
        

        然后你可以格式化你的变量传递一个数组中的参数:

        Object[] messageArguments = {
            "Mars",
            new Integer(7),
            new Date()
        };
        

        你这样调用格式化程序:

        MessageFormat formatter = new MessageFormat("");
        formatter.setLocale(currentLocale);
        formatter.applyPattern(messages.getString("template"));
        String output = formatter.format(messageArguments);
        

        详细的例子在这里 http://docs.oracle.com/javase/tutorial/i18n/format/messageFormat.html

        【讨论】:

          【解决方案4】:

          你想要String.format方法。

          String data  = "Hello, %s";
          String updated = String.format(data, "Felix");
          

          【讨论】:

            【解决方案5】:

            AFAIK 您可以为此使用String#format

            String name = "Felix";
            String s = String.format("Hello, %s", name);
            System.out.println(s);
            

            这将打印出来

            Hello, Felix
            

            有关如何使用String#format 格式的更多信息,请访问java.util.Formatter syntax

            【讨论】:

              猜你喜欢
              • 2020-02-14
              • 2015-11-25
              • 2017-03-09
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2023-02-12
              • 2012-06-12
              • 1970-01-01
              相关资源
              最近更新 更多