development

Java String에서 토큰 세트를 대체하는 방법은 무엇입니까?

big-blog 2020. 8. 12. 22:15
반응형

Java String에서 토큰 세트를 대체하는 방법은 무엇입니까?


다음 템플릿 String : "Hello [Name] Please find attached [Invoice Number] which is due on [Due Date]".

이름, 송장 번호 및 기한에 대한 문자열 변수도 있습니다. 템플릿의 토큰을 변수로 바꾸는 가장 좋은 방법은 무엇입니까?

(변수에 토큰이 포함 된 경우이를 대체해서는 안됩니다.)


편집하다

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

가장 효율적인 방법은 매처를 사용하여 지속적으로 표현식을 찾아서 교체 한 다음 문자열 작성기에 텍스트를 추가하는 것입니다.

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

나는 이것을 위해 템플릿 엔진이나 그와 비슷한 것을 사용할 필요가 없다고 생각합니다. 다음 String.format과 같이 방법을 사용할 수 있습니다 .

String template = "Hello %s Please find attached %s which is due on %s";

String message = String.format(template, name, invoiceNumber, dueDate);

Unfortunately the comfortable method String.format mentioned above is only available starting with Java 1.5 (which should be pretty standard nowadays, but you never know). Instead of that you might also use Java's class MessageFormat for replacing the placeholders.

It supports placeholders in the form '{number}', so your message would look like "Hello {0} Please find attached {1} which is due on {2}". These Strings can easily be externalized using ResourceBundles (e. g. for localization with multiple locales). The replacing would be done using the static'format' method of class MessageFormat:

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

You could try using a templating library like Apache Velocity.

http://velocity.apache.org/

Here is an example:

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

The output would be:

Hello Mark. Please find attached invoice 42123 which is due on June 6, 2009.

You can use template library for complex template replacement.

FreeMarker is a very good choice.

http://freemarker.sourceforge.net/

But for simple task, there is a simple utility class can help you.

org.apache.commons.lang3.text.StrSubstitutor

It is very powerful, customizable, and easy to use.

This class takes a piece of text and substitutes all the variables within it. The default definition of a variable is ${variableName}. The prefix and suffix can be changed via constructors and set methods.

Variable values are typically resolved from a map, but could also be resolved from system properties, or by supplying a custom variable resolver.

For example, if you want to substitute system environment variable into a template string, here is the code:

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

System.out.println(MessageFormat.format("Hello {0}! You have {1} messages", "Join",10L));

Output: Hello Join! You have 10 messages"


String.format("Hello %s Please find attached %s which is due on %s", name, invoice, date)

It depends of where the actual data that you want to replace is located. You might have a Map like this:

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

containing all the data that can be replaced. Then you can iterate over the map and change everything in the String as follows:

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

You could also iterate over the String and find the elements in the map. But that is a little bit more complicated because you need to parse the String searching for the []. You could do it with a regular expression using Pattern and Matcher.


My solution for replacing ${variable} style tokens (inspired by the answers here and by the 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();
}

http://github.com/niesfisch/tokenreplacer


FYI

In the new language Kotlin, you can use "String Templates" in your source code directly, no 3rd party library or template engine need to do the variable replacement.

It is a feature of the language itself.

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


In the past, I've solved this kind of problem with StringTemplate and Groovy Templates.

Ultimately, the decision of using a templating engine or not should be based on the following factors:

  • Will you have many of these templates in the application?
  • Do you need the ability to modify the templates without restarting the application?
  • Who will be maintaining these templates? A Java programmer or a business analyst involved on the project?
  • Will you need to the ability to put logic in your templates, like conditional text based on values in the variables?
  • Will you need the ability to include other templates in a template?

If any of the above applies to your project, I would consider using a templating engine, most of which provide this functionality, and more.


I used

String template = "Hello %s Please find attached %s which is due on %s";

String message = String.format(template, name, invoiceNumber, dueDate);

With Apache Commons Library, you can simply use Stringutils.replaceEach:

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

From the documentation:

Replaces all occurrences of Strings within another String.

A null reference passed to this method is a no-op, or if any "search string" or "string to replace" is null, that replace will be ignored. This will not repeat. For repeating replaces, call the overloaded method.

 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"

참고URL : https://stackoverflow.com/questions/959731/how-to-replace-a-set-of-tokens-in-a-java-string

반응형