Один из способов, которыми Вы используете одиночный элемент, состоит в том, чтобы покрыть экземпляр, где должен быть единственный "брокер", управляющий доступом к ресурсу. Одиночные элементы хороши в регистраторах, потому что они посредничают в доступе к, скажем, файлу, который может только быть записан в исключительно. Для чего-то как вход они дают возможность абстракции далеко записей к чему-то как файл журнала - Вы могли обернуть механизм кэширования к своему одиночному элементу, и т.д.
Также думают о ситуации, где у Вас есть приложение со многими окнами/потоками/и т.д., но которому нужна единственная точка коммуникации. Я когда-то использовал для управления заданиями, которые я хотел, чтобы мое приложение запустило. Одиночный элемент был ответственен за сериализацию заданий и отображение их состояния к любой другой части программы, которой было интересно. В этом виде сценария можно посмотреть на одиночный элемент, как являющийся видом подобных выполнение класса "сервера" в приложении... HTH
Попробуйте следующее:
Примечание: Окончательное решение автора основано на этом примере и является гораздо более кратким.
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();
}
}
var wordCount =
from word in words
group word by word into g
select new { g.Key, Count = g.Count() };
Это взято из одного из примеров в linqpad
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 получает значение из карты и преобразует его в строку и выдает исключение, если его нет.
Обратите внимание, что я изменил шаблон, чтобы найти пустые фигурные скобки (например, {}), так как это явным образом проверяется условие ошибки.
EDIT: Обратите внимание, что appendReplacement не зависит от содержимого строки. Согласно javadocs, он распознает $ и обратную косую черту как специальный символ, поэтому я добавил несколько экранирований, чтобы обработать это в примере выше. Это сделано не самым осознанным образом, но в моем случае это не настолько большая проблема, чтобы стоить попытки микрооптимизировать создание строк.
Благодаря комментарию Алана М, это можно сделать еще проще. чтобы избежать проблем со специальными символами при добавлении замены.
Обратите внимание, что я изменил шаблон, чтобы найти пустые фигурные скобки (т. Е. {}), Поскольку это явное условие ошибки.
РЕДАКТИРОВАТЬ: Обратите внимание, что appendReplacement не зависит от содержимого строки. Согласно javadocs, он распознает $ и обратную косую черту как специальный символ, поэтому я добавил несколько экранирований, чтобы обработать это в примере выше. Это сделано не самым осознанным образом, но в моем случае это не настолько большая проблема, чтобы стоить попытки микрооптимизировать создание строк.
Благодаря комментарию Алана М, это можно сделать еще проще. чтобы избежать проблем со специальными символами при добавлении замены.
Обратите внимание, что я изменил шаблон, чтобы найти пустые фигурные скобки (т. Е. {}), Поскольку это явное условие ошибки.
РЕДАКТИРОВАТЬ: Обратите внимание, что appendReplacement не зависит от содержимого строки. Согласно javadocs, он распознает $ и обратную косую черту как специальный символ, поэтому я добавил несколько экранирований, чтобы обработать это в примере выше. Это сделано не самым осознанным образом, но в моем случае это не настолько большая проблема, чтобы стоить попытки микрооптимизировать создание строк.
Благодаря комментарию Алана М это можно сделать еще проще. чтобы избежать проблем со специальными символами при добавлении замены.
он распознает $ и обратную косую черту как специальный символ, поэтому я добавил несколько экранирований, чтобы обработать это в примере выше. Это сделано не самым осознанным образом, но в моем случае это не настолько большая проблема, чтобы стоить попытки микрооптимизировать создание строк.Благодаря комментарию Алана М, это можно сделать еще проще. чтобы избежать проблем со специальными символами при добавлении замены.
он распознает $ и обратную косую черту как специальный символ, поэтому я добавил несколько экранирований, чтобы обработать это в примере выше. Это сделано не самым осознанным образом, но в моем случае это не настолько большая проблема, чтобы стоить попытки микрооптимизировать создание строк.Благодаря комментарию Алана М, это можно сделать еще проще. чтобы избежать проблем со специальными символами при добавлении замены.
String.replaceAll("{FIRST_NAME}", actualName);
Ознакомьтесь с javadocs для этого здесь .
Что ж, я бы предпочел использовать String.format () или лучше MessageFormat .
С 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());
Но это, вероятно, излишне, если вы хотите заменить только одно или два значения.
Документы означают, что вам следует предпочесть писать токенизатор на основе регулярных выражений, IIRC. Что может подойти вам лучше, так это стандартный поиск-замена регулярного выражения.
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);
}
}
Обычно в таком случае мы использовали бы MessageFormat вместе с загрузкой фактического текста сообщения из ResourceBundle. Это дает вам дополнительное преимущество - дружелюбие к G10N.