我有以下模板字符串:"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();
}
答案 0 :(得分:98)
我真的认为你不需要使用模板引擎或类似的东西。您可以使用String.format
方法,如下所示:
String template = "Hello %s Please find attached %s which is due on %s";
String message = String.format(template, name, invoiceNumber, dueDate);
答案 1 :(得分:65)
最有效的方法是使用匹配器不断查找表达式并替换它们,然后将文本附加到字符串构建器:
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 :(得分:41)
您可以尝试使用像Apache Velocity这样的模板库。
以下是一个例子:
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.
答案 3 :(得分:41)
不幸的是,上面提到的舒适方法String.format只能从Java 1.5开始提供(现在应该是相当标准的,但你永远不知道)。除此之外,您还可以使用Java的class MessageFormat来替换占位符。
它支持“{number}”形式的占位符,因此您的消息看起来像“Hello {0}请找到{2}上应该附加的{1}”。可以使用ResourceBundles轻松地将这些字符串外部化(例如,用于具有多个语言环境的本地化)。替换将使用MessageFormat类的static'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 :(得分:23)
您可以使用模板库进行复杂的模板替换。
FreeMarker是一个非常好的选择。
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);
}
}
答案 5 :(得分:16)
System.out.println(MessageFormat.format("Hello {0}! You have {1} messages", "Join",10L));
<强>输出:强> 你好加入!你有10条消息“
答案 6 :(得分:9)
String.format("Hello %s Please find attached %s which is due on %s", name, invoice, date)
答案 7 :(得分:8)
这取决于您要替换的实际数据所在的位置。你可能有这样的地图:
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());
}
您还可以遍历String并在地图中查找元素。但这有点复杂,因为你需要解析String搜索[]。你可以使用Pattern和Matcher以正则表达式来完成它。
答案 8 :(得分:3)
我的替换$ {variable}样式标记的解决方案(灵感来自这里和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();
}
答案 9 :(得分:1)
答案 10 :(得分:1)
FYI
用新语言Kotlin, 你可以使用&#34;字符串模板&#34;直接在你的源代码中, 没有第三方库或模板引擎需要进行变量替换。
这是语言本身的一个特征。
请参阅: https://kotlinlang.org/docs/reference/basic-types.html#string-templates
答案 11 :(得分:0)
过去,我使用StringTemplate和Groovy Templates解决了这类问题。
最终,使用模板引擎的决定应基于以下因素:
如果上述任何一项适用于您的项目,我会考虑使用模板引擎,其中大部分都提供此功能,等等。
答案 12 :(得分:0)
我用过
String template = "Hello %s Please find attached %s which is due on %s";
String message = String.format(template, name, invoiceNumber, dueDate);
答案 13 :(得分:0)
使用Apache Commons Library,您只需使用Stringutils.replaceEach:
public static String replaceEach(String text,
String[] searchList,
String[] replacementList)
在另一个字符串中替换所有出现的字符串。
传递给此方法的空引用是无操作,或者如果有任何&#34;搜索 字符串&#34;或&#34;要替换的字符串&#34;为null,将替换该替换。 这不会重复。要重复替换,请调用重载 方法
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"
答案 14 :(得分:0)
以下代码将<<VAR>>
形式的变量替换为从Map查找的值。您可以test it online here
例如,使用以下输入字符串
BMI=(<<Weight>>/(<<Height>>*<<Height>>)) * 70
Hi there <<Weight>> was here
以及以下变量值
Weight, 42
Height, HEIGHT 51
输出以下内容
BMI=(42/(HEIGHT 51*HEIGHT 51)) * 70
Hi there 42 was here
这是代码
static Pattern pattern = Pattern.compile("<<([a-z][a-z0-9]*)>>", Pattern.CASE_INSENSITIVE);
public static String replaceVarsWithValues(String message, Map<String,String> varValues) {
try {
StringBuffer newStr = new StringBuffer(message);
int lenDiff = 0;
Matcher m = pattern.matcher(message);
while (m.find()) {
String fullText = m.group(0);
String keyName = m.group(1);
String newValue = varValues.get(keyName)+"";
String replacementText = newValue;
newStr = newStr.replace(m.start() - lenDiff, m.end() - lenDiff, replacementText);
lenDiff += fullText.length() - replacementText.length();
}
return newStr.toString();
} catch (Exception e) {
return message;
}
}
public static void main(String args[]) throws Exception {
String testString = "BMI=(<<Weight>>/(<<Height>>*<<Height>>)) * 70\n\nHi there <<Weight>> was here";
HashMap<String,String> values = new HashMap<>();
values.put("Weight", "42");
values.put("Height", "HEIGHT 51");
System.out.println(replaceVarsWithValues(testString, values));
}
尽管没有要求,但您可以使用类似的方法用application.properties文件中的属性替换字符串中的变量,尽管这可能已经完成了:
private static Pattern patternMatchForProperties =
Pattern.compile("[$][{]([.a-z0-9_]*)[}]", Pattern.CASE_INSENSITIVE);
protected String replaceVarsWithProperties(String message) {
try {
StringBuffer newStr = new StringBuffer(message);
int lenDiff = 0;
Matcher m = patternMatchForProperties.matcher(message);
while (m.find()) {
String fullText = m.group(0);
String keyName = m.group(1);
String newValue = System.getProperty(keyName);
String replacementText = newValue;
newStr = newStr.replace(m.start() - lenDiff, m.end() - lenDiff, replacementText);
lenDiff += fullText.length() - replacementText.length();
}
return newStr.toString();
} catch (Exception e) {
return message;
}
}