Java 如何在没有StringTokenizer的情况下替换字符串中的标记

Java 如何在没有StringTokenizer的情况下替换字符串中的标记,java,regex,stringtokenizer,Java,Regex,Stringtokenizer,给定这样的字符串: Hello {FIRST_NAME}, this is a personalized message for you. 其中FIRST_NAME是一个任意标记(传递给方法的映射中的键),用于编写一个例程,将该字符串转换为: Hello Jim, this is a personalized message for you. 给定一张地图,上面有一个条目FIRST_NAME->Jim StringTokenizer似乎是最直截了当的方法,但Javadoc确实说您应该更喜欢

给定这样的字符串:

 Hello {FIRST_NAME}, this is a personalized message for you.
其中FIRST_NAME是一个任意标记(传递给方法的映射中的键),用于编写一个例程,将该字符串转换为:

Hello Jim, this is a personalized message for you.
给定一张地图,上面有一个条目FIRST_NAME->Jim


StringTokenizer似乎是最直截了当的方法,但Javadoc确实说您应该更喜欢使用regex aproach。在基于正则表达式的解决方案中,您将如何做到这一点?

文档意味着您应该更喜欢编写基于正则表达式的标记器IIRC。标准的正则表达式搜索替换可能更适合您

String.replaceAll("{FIRST_NAME}", actualName);

请查看javadocs。

好吧,我宁愿使用String.format(),或者更好。

试试这个:

注意:本示例以该示例为基础,更加简洁

public class TokenReplacer {

    private Pattern tokenPattern;

    public TokenReplacer() {
        tokenPattern = Pattern.compile("\\{([^}]+)\\}");
    }

    public String replaceTokens(String text, Map<String, String> valuesByKey) {
        StringBuilder output = new StringBuilder();
        Matcher tokenMatcher = tokenPattern.matcher(text);

        int cursor = 0;
        while (tokenMatcher.find()) {
            // A token is defined as a sequence of the format "{...}".
            // A key is defined as the content between the brackets.
            int tokenStart = tokenMatcher.start();
            int tokenEnd = tokenMatcher.end();
            int keyStart = tokenMatcher.start(1);
            int keyEnd = tokenMatcher.end(1);

            output.append(text.substring(cursor, tokenStart));

            String token = text.substring(tokenStart, tokenEnd);
            String key = text.substring(keyStart, keyEnd);

            if (valuesByKey.containsKey(key)) {
                String value = valuesByKey.get(key);
                output.append(value);
            } else {
                output.append(token);
            }

            cursor = tokenEnd;
        }
        output.append(text.substring(cursor));

        return output.toString();
    }

}
公共类令牌替换器{
私有模式;
公共令牌替换器(){
tokenPattern=Pattern.compile(“\\{([^}]+)\\}”);
}
公共字符串替换令牌(字符串文本、映射值bykey){
StringBuilder输出=新的StringBuilder();
Matcher-tokenMatcher=tokenPattern.Matcher(文本);
int游标=0;
while(tokenMatcher.find()){
//令牌被定义为格式为“{…}”的序列。
//键定义为括号之间的内容。
int tokenStart=tokenMatcher.start();
int tokenEnd=tokenMatcher.end();
int keyStart=tokenMatcher.start(1);
int keyEnd=tokenMatcher.end(1);
append(text.substring(游标,tokenStart));
String token=text.substring(tokenStart,tokenEnd);
String key=text.substring(keyStart,keyEnd);
if(值bykey.containsKey(键)){
字符串值=valuesByKey.get(键);
输出。追加(值);
}否则{
输出。追加(令牌);
}
游标=标记结束;
}
output.append(text.substring(游标));
返回output.toString();
}
}

最直截了当的说法似乎是这样的:

public static void main(String[] args) {
    String tokenString = "Hello {FIRST_NAME}, this is a personalized message for you.";
    Map<String, String> tokenMap = new HashMap<String, String>();
    tokenMap.put("{FIRST_NAME}", "Jim");
    String transformedString = tokenString;
    for (String token : tokenMap.keySet()) {
        transformedString = transformedString.replace(token, tokenMap.get(token));
    }
    System.out.println("New String: " + transformedString);
}
publicstaticvoidmain(字符串[]args){
String tokenString=“你好{FIRST_NAME},这是您的个性化邮件。”;
Map tokenMap=newhashmap();
tokenMap.put(“{FIRST_NAME}”,“Jim”);
String transformedString=令牌字符串;
for(字符串标记:tokenMap.keySet()){
transformedString=transformedString.replace(token,tokenMap.get(token));
}
System.out.println(“新字符串:“+transformedString”);
}

它循环遍历您的所有令牌并用您需要的替换每个令牌,并使用标准字符串方法进行替换,从而跳过整个正则表达式的挫折。

使用import java.util.RegEx.*:

Pattern p = Pattern.compile("{([^{}]*)}");
Matcher m = p.matcher(line);  // line being "Hello, {FIRST_NAME}..."
while (m.find) {
  String key = m.group(1);
  if (map.containsKey(key)) {
    String value= map.get(key);
    m.replaceFirst(value);
  }
}
因此,建议使用正则表达式,因为它可以很容易地识别字符串中需要替换的位置,以及提取要替换的键的名称。这比断开整根绳子要有效得多


您可能希望在内部循环匹配线,外部循环模式线,以便替换所有线。模式永远不需要重新编译,而且避免不必要地重新编译更有效。

根据字符串的复杂程度,您可以尝试使用更严格的字符串模板语言,如Velocity。在Velocity的例子中,您可以这样做:

Velocity.init();
VelocityContext context = new VelocityContext();
context.put( "name", "Bob" );
StringWriter output = new StringWriter();
Velocity.evaluate( context, output, "", 
      "Hello, #name, this is a personalized message for you.");
System.out.println(output.toString());
但是,如果您只想替换一个或两个值,那么这可能就太过分了。

import java.util.HashMap;
import java.util.HashMap;

public class ReplaceTest {

  public static void main(String[] args) {
    HashMap<String, String> map = new HashMap<String, String>();

    map.put("FIRST_NAME", "Jim");
    map.put("LAST_NAME",  "Johnson");
    map.put("PHONE",      "410-555-1212");

    String s = "Hello {FIRST_NAME} {LAST_NAME}, this is a personalized message for you.";

    for (String key : map.keySet()) {
      s = s.replaceAll("\\{" + key + "\\}", map.get(key));
    }

    System.out.println(s);
  }

}
公共类替换测试{ 公共静态void main(字符串[]args){ HashMap=newHashMap(); 地图放置(“名字”、“吉姆”); 地图放置(“姓氏”、“约翰逊”); 地图放置(“电话”,“410-555-1212”); String s=“您好{FIRST_NAME}{LAST_NAME},这是一封针对您的个性化邮件。”; for(字符串键:map.keySet()){ s=s.replaceAll(“\\{”+key+“\\}”,map.get(key)); } 系统输出打印项次; } }
谢谢大家的回答

Gizmo的答案绝对是开箱即用的,这是一个很好的解决方案,但不幸的是并不合适,因为格式不能局限于Formatter类在本例中的作用

亚当·佩恩特(Adam Paynter)用正确的模式真正触及了问题的核心

Peter Nix和Sean Bright有一个很好的变通方法来避免正则表达式的所有复杂性,但如果存在坏的代币,我需要提出一些错误,但这并没有做到

但在执行正则表达式和合理的替换循环方面,这是我给出的答案(在谷歌和现有答案的帮助下,包括Sean Bright关于如何使用group(1)vs group()的评论):

private static Pattern-tokenPattern=Pattern.compile(“\\{([^}]*)\\}”);
公共静态字符串进程(字符串模板、映射参数){
StringBuffer sb=新的StringBuffer();
Matcher myMatcher=tokenPattern.Matcher(模板);
while(myMatcher.find()){
字符串字段=myMatcher.group(1);
myMatcher.附录替换(sb,“”);
sb.追加(doParameter(字段,参数));
}
我的配对者(某人);
使某人返回字符串();
}
其中doParameter从映射中获取值,并将其转换为字符串,如果不存在,则抛出异常

请注意,我还更改了模式以查找空大括号(即{}),因为这是显式检查的错误条件

编辑:请注意,appendReplacement对字符串的内容不是不可知的。根据javadocs,它将$和反斜杠识别为一个特殊字符,因此我在上面的示例中添加了一些转义来处理它。这不是以最注重性能的方式完成的,但就我而言,这还不够大,不值得尝试对字符串创作进行微优化

多亏了Alan M的评论,这可以变得更简单,以避免特殊的字符
private static Pattern tokenPattern = Pattern.compile("\\{([^}]*)\\}");

public static String process(String template, Map<String, Object> params) {
    StringBuffer sb = new StringBuffer();
    Matcher myMatcher = tokenPattern.matcher(template);
    while (myMatcher.find()) {
        String field = myMatcher.group(1);
        myMatcher.appendReplacement(sb, "");
        sb.append(doParameter(field, params));
   }
    myMatcher.appendTail(sb);
    return sb.toString();
}