[英]How to replace tokens in a string without 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是最直接的方法,但Javadocs真的說你應該更喜歡使用正則表達式aproach。 你會如何在基於正則表達式的解決方案中做到這一點?
謝謝大家的答案!
Gizmo的答案絕對是開箱即用的,也是一個很好的解決方案,但遺憾的是不適合,因為格式不能局限於Formatter類在這種情況下的作用。
Adam Paynter真正了解問題的核心,采用正確的模式。
Peter Nix和Sean Bright有一個很好的解決方法來避免正則表達式的所有復雜性,但是如果有不好的令牌,那么我需要提出一些錯誤。
但就完成正則表達式和合理的替換循環而言,這是我提出的答案(谷歌和現有答案的一點幫助,包括Sean Bright關於如何使用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();
}
doParameter從地圖中獲取值並將其轉換為字符串,如果不存在則拋出異常。
另請注意,我更改了模式以查找空括號(即{}),因為這是顯式檢查的錯誤條件。
編輯:
請注意,appendReplacement與字符串的內容無關。
根據javadoc,它將$和反斜杠識別為一個特殊字符,因此我添加了一些轉義來處理上面的示例。
沒有以最具表現意識的方式完成,但在我的情況下,值得嘗試微量優化弦樂創作並不是一件足夠大的事情。
感謝Alan M的評論,可以更簡單地避免appendReplacement的特殊字符問題。
好吧,我寧願使用String.format(),也不想使用更好的MessageFormat 。
String.replaceAll("{FIRST_NAME}", actualName);
在這里查看javadocs。
試試這個:
注意: 作者的最終解決方案建立在此示例的基礎上,並且更加簡潔。
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();
}
}
使用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);
}
}
因此,建議使用正則表達式,因為它可以輕松識別字符串中需要替換的位置,以及提取替換鍵的名稱。 它比打破整個字符串更有效率。
您可能希望循環使用內部的Matcher線和外部的Pattern線,這樣您就可以替換所有線。 該模式永遠不需要重新編譯,並且避免不必要地這樣做更有效。
最直接的似乎是這樣的:
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);
}
它遍歷所有令牌並用您需要的內容替換每個令牌,並使用標准的String方法進行替換,從而跳過整個RegEx挫折。
根據字符串的復雜程度,您可以嘗試使用更嚴格的字符串模板語言,如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;
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);
}
}
文檔意味着您應該更喜歡編寫基於正則表達式的標記化器IIRC。 什么可能更適合你是一個標准的正則表達式搜索替換。
通常我們在這種情況下使用MessageFormat,同時從ResourceBundle加載實際的消息文本。 這為您提供了G10N友好的額外好處。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.