Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to replace tokens in a string without StringTokenizer

Given a string like so:

 Hello {FIRST_NAME}, this is a personalized message for you.

Where FIRST_NAME is an arbitrary token (a key in a map passed to the method), to write a routine which would turn that string into:

Hello Jim, this is a personalized message for you.

given a map with an entry FIRST_NAME -> Jim.

It would seem that StringTokenizer is the most straight forward approach, but the Javadocs really say you should prefer to use the regex aproach. How would you do that in a regex based solution?

like image 227
Yishai Avatar asked Jul 16 '09 16:07

Yishai


People also ask

How do you replace a word in a string in Java without using replace method?

To replace a character in a String, without using the replace() method, try the below logic. Let's say the following is our string. int pos = 7; char rep = 'p'; String res = str. substring(0, pos) + rep + str.

How do you replace a token in a string in Java?

To substitute tokens in a String in Java, we use the Message Format class. The Message Format class provides a means to produce concatenated messages which are not dependent on the language. The Message Format class extends the Serializable and Cloneable interfaces.

Why is StringTokenizer deprecated?

split() , or whether the deprecation is purely a matter of convenience and my code is safe. StringTokenizer is a legacy class (i.e. there is a better replacement out there), but it's not deprecated. Deprecation only happens when the class/method has some serious drawbacks.


2 Answers

Thanks everyone for the answers!

Gizmo's answer was definitely out of the box, and a great solution, but unfortunately not appropriate as the format can't be limited to what the Formatter class does in this case.

Adam Paynter really got to the heart of the matter, with the right pattern.

Peter Nix and Sean Bright had a great workaround to avoid all of the complexities of the regex, but I needed to raise some errors if there were bad tokens, which that didn't do.

But in terms of both doing a regex and a reasonable replace loop, this is the answer I came up with (with a little help from Google and the existing answer, including Sean Bright's comment about how to use group(1) vs group()):

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();
}

Where doParameter gets the value out of the map and converts it to a string and throws an exception if it isn't there.

Note also I changed the pattern to find empty braces (i.e. {}), as that is an error condition explicitly checked for.

EDIT: Note that appendReplacement is not agnostic about the content of the string. Per the javadocs, it recognizes $ and backslash as a special character, so I added some escaping to handle that to the sample above. Not done in the most performance conscious way, but in my case it isn't a big enough deal to be worth attempting to micro-optimize the string creations.

Thanks to the comment from Alan M, this can be made even simpler to avoid the special character issues of appendReplacement.

like image 167
Yishai Avatar answered Sep 29 '22 03:09

Yishai


Well, I would rather use String.format(), or better MessageFormat.

like image 32
gizmo Avatar answered Sep 29 '22 04:09

gizmo