代码之家  ›  专栏  ›  技术社区  ›  Mark

如何替换Java字符串中的一组令牌?

  •  100
  • Mark  · 技术社区  · 15 年前

    我有以下模板字符串: "Hello [Name] Please find attached [Invoice Number] which is due on [Due Date]" .

    我还具有用于名称、发票编号和到期日期的字符串变量-用变量替换模板中的令牌的最佳方法是什么?

    (请注意,如果一个变量恰好包含一个标记,则不应替换它)。


    编辑

    感谢@laginimaneb和@alan moore, 我的解决方案是:

    public static String replaceTokens(String text, 
                                       Map<String, String> replacements) {
        Pattern pattern = Pattern.compile("\\[(.+?)\\]");
        Matcher matcher = pattern.matcher(text);
        StringBuffer buffer = new StringBuffer();
    
        while (matcher.find()) {
            String replacement = replacements.get(matcher.group(1));
            if (replacement != null) {
                // matcher.appendReplacement(buffer, replacement);
                // see comment 
                matcher.appendReplacement(buffer, "");
                buffer.append(replacement);
            }
        }
        matcher.appendTail(buffer);
        return buffer.toString();
    }
    
    14 回复  |  直到 7 年前
        1
  •  63
  •   laginimaineb    15 年前

    最有效的方法是使用matcher不断地查找表达式并替换它们,然后将文本附加到字符串生成器:

    Pattern pattern = Pattern.compile("\\[(.+?)\\]");
    Matcher matcher = pattern.matcher(text);
    HashMap<String,String> replacements = new HashMap<String,String>();
    //populate the replacements map ...
    StringBuilder builder = new StringBuilder();
    int i = 0;
    while (matcher.find()) {
        String replacement = replacements.get(matcher.group(1));
        builder.append(text.substring(i, matcher.start()));
        if (replacement == null)
            builder.append(matcher.group(0));
        else
            builder.append(replacement);
        i = matcher.end();
    }
    builder.append(text.substring(i, text.length()));
    return builder.toString();
    
        2
  •  94
  •   Paul Morie    15 年前

    我真的不认为你需要使用模板引擎或者类似的东西来完成这个任务。您可以使用 String.format 方法,如:

    String template = "Hello %s Please find attached %s which is due on %s";
    
    String message = String.format(template, name, invoiceNumber, dueDate);
    
        3
  •  41
  •   toolkit    15 年前

    不幸的是,上面提到的舒适方法Strug.Frad只能从Java 1.5开始使用(现在应该是非常标准的,但你永远不知道)。相反,你也可以使用Java的 class MessageFormat 用于替换占位符。

    它支持“数字”形式的占位符,因此您的消息将显示为“您好0请查找附件1,该附件将于2到期”。使用ResourceBundles可以很容易地将这些字符串外部化(例如,对于具有多个区域设置的本地化)。替换将使用messageformat类的静态“format”方法完成:

    String msg = "Hello {0} Please find attached {1} which is due on {2}";
    String[] values = {
      "John Doe", "invoice #123", "2009-06-30"
    };
    System.out.println(MessageFormat.format(msg, values));
    
        4
  •  40
  •   hallidave    15 年前

    您可以尝试使用模板库,比如ApacheVelocity。

    http://velocity.apache.org/

    下面是一个例子:

    import org.apache.velocity.VelocityContext;
    import org.apache.velocity.app.Velocity;
    
    import java.io.StringWriter;
    
    public class TemplateExample {
        public static void main(String args[]) throws Exception {
            Velocity.init();
    
            VelocityContext context = new VelocityContext();
            context.put("name", "Mark");
            context.put("invoiceNumber", "42123");
            context.put("dueDate", "June 6, 2009");
    
            String template = "Hello $name. Please find attached invoice" +
                              " $invoiceNumber which is due on $dueDate.";
            StringWriter writer = new StringWriter();
            Velocity.evaluate(context, writer, "TemplateName", template);
    
            System.out.println(writer);
        }
    }
    

    输出将是:

    Hello Mark. Please find attached invoice 42123 which is due on June 6, 2009.
        5
  •  23
  •   OneCricketeer    8 年前

    您可以使用模板库进行复杂的模板替换。

    自由标记是一个很好的选择。

    http://freemarker.sourceforge.net/

    但是对于简单的任务,有一个简单的实用程序类可以帮助您。

    org.apache.commons.lang3.text.StrSubstitutor

    它功能强大,可定制,易于使用。

    这个类接受一段文本并替换所有变量 在它里面。变量的默认定义是$variablename。 前缀和后缀可以通过构造函数和set方法更改。

    变量值通常从映射中解析,但也可以 通过系统属性或提供自定义变量来解析 旋转变压器。

    例如,如果要将系统环境变量替换为模板字符串, 代码如下:

    public class SysEnvSubstitutor {
        public static final String replace(final String source) {
            StrSubstitutor strSubstitutor = new StrSubstitutor(
                    new StrLookup<Object>() {
                        @Override
                        public String lookup(final String key) {
                            return System.getenv(key);
                        }
                    });
            return strSubstitutor.replace(source);
        }
    }
    
        6
  •  16
  •   Nazik Giovanni    11 年前
    System.out.println(MessageFormat.format("Hello {0}! You have {1} messages", "Join",10L));
    

    输出: 你好,加入!你有10条信息”

        7
  •  9
  •   Pablo Fernandez    13 年前
    String.format("Hello %s Please find attached %s which is due on %s", name, invoice, date)
    
        8
  •  8
  •   Community Egal    13 年前

    这取决于要替换的实际数据的位置。你可能有这样的地图:

    Map<String, String> values = new HashMap<String, String>();
    

    包含所有可以替换的数据。然后,您可以迭代映射并按如下方式更改字符串中的所有内容:

    String s = "Your String with [Fields]";
    for (Map.Entry<String, String> e : values.entrySet()) {
      s = s.replaceAll("\\[" + e.getKey() + "\\]", e.getValue());
    }
    

    您还可以迭代字符串并在映射中找到元素。但这有点复杂,因为您需要解析字符串来搜索[]。您可以使用模式和匹配器使用正则表达式来实现这一点。

        9
  •  3
  •   mihu86    11 年前

    我的替换$变量样式令牌的解决方案(受此处答案和Spring UriTemplate的启发):

    public static String substituteVariables(String template, Map<String, String> variables) {
        Pattern pattern = Pattern.compile("\\$\\{(.+?)\\}");
        Matcher matcher = pattern.matcher(template);
        // StringBuilder cannot be used here because Matcher expects StringBuffer
        StringBuffer buffer = new StringBuffer();
        while (matcher.find()) {
            if (variables.containsKey(matcher.group(1))) {
                String replacement = variables.get(matcher.group(1));
                // quote to work properly with $ and {,} signs
                matcher.appendReplacement(buffer, replacement != null ? Matcher.quoteReplacement(replacement) : "null");
            }
        }
        matcher.appendTail(buffer);
        return buffer.toString();
    }
    
        11
  •  1
  •   Li Ying    7 年前

    供参考

    在新的语言Kotlin中, 您可以直接在源代码中使用“字符串模板”, 没有第三方库或模板引擎需要进行变量替换。

    它是语言本身的一个特征。

    见: https://kotlinlang.org/docs/reference/basic-types.html#string-templates

        12
  •  0
  •   Francois Gravel    15 年前

    过去,我用 StringTemplate Groovy Templates .

    最终,是否使用模板化引擎的决定应基于以下因素:

    • 您在应用程序中会有许多这样的模板吗?
    • 是否需要在不重新启动应用程序的情况下修改模板的功能?
    • 谁将维护这些模板?项目中涉及的Java程序员或业务分析师?
    • 您是否需要能够将逻辑放入模板中,比如基于变量值的条件文本?
    • 您是否需要在模板中包含其他模板的功能?

    如果上面的任何一个应用于您的项目,我将考虑使用模板化引擎,其中大部分提供了这个功能,等等。

        13
  •  0
  •   mtwom    13 年前

    我用过

    String template = "Hello %s Please find attached %s which is due on %s";
    
    String message = String.format(template, name, invoiceNumber, dueDate);
    
        14
  •  0
  •   AR1    7 年前

    使用ApacheCommons库,您可以简单地使用 Stringutils.replaceEach :

    public static String replaceEach(String text,
                                 String[] searchList,
                                 String[] replacementList)
    

    documentation :

    替换另一个字符串中出现的所有字符串。

    传递给此方法的空引用是no op,或者如果有“search” string“或”string to replace“为空,将忽略该替换。 这不会重复。对于重复替换,调用重载 方法。

     StringUtils.replaceEach(null, *, *)        = null
    
      StringUtils.replaceEach("", *, *)          = ""
    
      StringUtils.replaceEach("aba", null, null) = "aba"
    
      StringUtils.replaceEach("aba", new String[0], null) = "aba"
    
      StringUtils.replaceEach("aba", null, new String[0]) = "aba"
    
      StringUtils.replaceEach("aba", new String[]{"a"}, null)  = "aba"
    
      StringUtils.replaceEach("aba", new String[]{"a"}, new String[]{""})  = "b"
    
      StringUtils.replaceEach("aba", new String[]{null}, new String[]{"a"})  = "aba"
    
      StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"w", "t"})  = "wcte"
      (example of how it does not repeat)
    
    StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "t"})  = "dcte"