J'ai le modèle de chaîne suivant: "Hello [Name] Please find attached [Invoice Number] which is due on [Due Date]"
.
J'ai également des variables de chaîne pour le nom, le numéro de facture et la date d'échéance. Quelle est la meilleure façon de remplacer les jetons du modèle par les variables?
(Notez que si une variable contient un jeton, elle ne doit PAS être remplacée).
MODIFIER
Avec merci à @laginimaineb et @ alan-moore, voici ma solution:
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();
}
La méthode la plus efficace consiste à utiliser un matcher pour rechercher en permanence les expressions et les remplacer, puis ajouter le texte à un constructeur de chaînes:
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();
Je ne pense vraiment pas que vous ayez besoin d'utiliser un moteur de template ou quelque chose comme ça pour ça. Vous pouvez utiliser la méthode String.format
, comme suit:
String template = "Hello %s Please find attached %s which is due on %s";
String message = String.format(template, name, invoiceNumber, dueDate);
Malheureusement, la méthode confortable String.format mentionnée ci-dessus n'est disponible qu'à partir de Java 1.5 (qui devrait être plutôt standard de nos jours, mais on ne sait jamais). Au lieu de cela, vous pouvez également utiliser la classe MessageFormat de Java pour remplacer les espaces réservés.
Il prend en charge les espaces réservés sous la forme '{numéro}', afin que votre message ressemble à "Bonjour {0}. Veuillez trouver le fichier attaché {1} qui doit être reçu le {2}". Ces chaînes peuvent facilement être externalisées à l'aide de ResourceBundles (par exemple, pour la localisation avec plusieurs paramètres régionaux). Le remplacement se ferait à l'aide de la méthode static'format 'de la classe 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));
Vous pouvez essayer d'utiliser une bibliothèque de modèles comme Apache Velocity.
Voici un exemple:
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);
}
}
La sortie serait:
Bonjour Mark. Veuillez trouver ci-joint la facture 42123, qui est due le 6 juin 2009.
Vous pouvez utiliser la bibliothèque de modèles pour le remplacement de modèles complexes.
FreeMarker est un très bon choix.
http://freemarker.sourceforge.net/
Mais pour une tâche simple, il existe une classe utilitaire simple qui peut vous aider.
org.Apache.commons.lang3.text.StrSubstitutor
Il est très puissant, personnalisable et facile à utiliser.
Cette classe prend un morceau de texte et remplace toutes les variables à l'intérieur. La définition par défaut d'une variable est $ {nomVariable} . Le préfixe et le suffixe peuvent être modifiés via des constructeurs et des méthodes set.
Les valeurs de variables sont généralement résolues à partir d'une carte, mais pourraient également être résolus à partir des propriétés du système, ou en fournissant une variable personnalisée résolveur.
Par exemple, si vous souhaitez substituer la variable d'environnement système dans une chaîne de modèle, Voici le 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));
Sortie: Hello Join! Vous avez 10 messages "
String.format("Hello %s Please find attached %s which is due on %s", name, invoice, date)
Cela dépend de l'emplacement des données réelles que vous souhaitez remplacer. Vous pourriez avoir une carte comme celle-ci:
Map<String, String> values = new HashMap<String, String>();
contenant toutes les données pouvant être remplacées. Ensuite, vous pouvez parcourir la carte et tout modifier dans la chaîne comme suit:
String s = "Your String with [Fields]";
for (Map.Entry<String, String> e : values.entrySet()) {
s = s.replaceAll("\\[" + e.getKey() + "\\]", e.getValue());
}
Vous pouvez également parcourir la chaîne et rechercher les éléments dans la carte. Mais c'est un peu plus compliqué parce que vous devez analyser la chaîne à la recherche du []. Vous pouvez le faire avec une expression régulière en utilisant Pattern et Matcher.
Ma solution pour remplacer les jetons de style $ {variable} (inspirés des réponses fournies ici et par le 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();
}
FYI
Dans la nouvelle langue Kotlin, , Vous pouvez directement utiliser des "Modèles de chaîne" dans votre code source.
C'est une caractéristique de la langue elle-même.
Voir: https://kotlinlang.org/docs/reference/basic-types.html#string-templates
J'ai utilisé
String template = "Hello %s Please find attached %s which is due on %s";
String message = String.format(template, name, invoiceNumber, dueDate);
Dans le passé, j'avais résolu ce genre de problème avec StringTemplate et Groovy Templates .
En fin de compte, la décision d'utiliser ou non un moteur de gabarit devrait être basée sur les facteurs suivants:
Si l’une des situations décrites ci-dessus s’applique à votre projet, j’envisagerais d’utiliser un moteur de modélisation, dont la plupart fournissent cette fonctionnalité, et plus encore.
Avec Apache Commons Library, vous pouvez simplement utiliser Stringutils.replaceEach :
public static String replaceEach(String text,
String[] searchList,
String[] replacementList)
De la documentation :
Remplace toutes les occurrences de chaînes dans une autre chaîne.
Une référence null transmise à cette méthode est une opération no-op ou, si une "recherche Chaîne" ou une "chaîne à remplacer" est null, cette substitution sera ignorée . Cela ne se répète pas. Pour répéter remplace, appelez le surchargé méthode.
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"