Related
I need to split a string in Java (first remove whitespaces between quotes and then split at whitespaces.)
"abc test=\"x y z\" magic=\" hello \" hola"
becomes:
firstly:
"abc test=\"xyz\" magic=\"hello\" hola"
and then:
abc
test="xyz"
magic="hello"
hola
Scenario :
I am getting a string something like above from input and I want to break it into parts as above. One way to approach was first remove the spaces between quotes and then split at spaces. Also string before quotes complicates it. Second one was split at spaces but not if inside quote and then remove spaces from individual split. I tried capturing quotes with "\"([^\"]+)\"" but I'm not able to capture just the spaces inside quotes. I tried some more but no luck.
We can do this using a formal pattern matcher. The secret sauce of the answer below is to use the not-much-used Matcher#appendReplacement method. We pause at each match, and then append a custom replacement of anything appearing inside two pairs of quotes. The custom method removeSpaces() strips all whitespace from each quoted term.
public static String removeSpaces(String input) {
return input.replaceAll("\\s+", "");
}
String input = "abc test=\"x y z\" magic=\" hello \" hola";
Pattern p = Pattern.compile("\"(.*?)\"");
Matcher m = p.matcher(input);
StringBuffer sb = new StringBuffer("");
while (m.find()) {
m.appendReplacement(sb, "\"" + removeSpaces(m.group(1)) + "\"");
}
m.appendTail(sb);
String[] parts = sb.toString().split("\\s+");
for (String part : parts) {
System.out.println(part);
}
abc
test="xyz"
magic="hello"
hola
Demo
The big caveat here, as the above comments hinted at, is that we are really using a regex engine as a rudimentary parser. To see where my solution would fail fast, just remove one of the quotes by accident from a quoted term. But, if you are sure you input is well formed as you have showed us, this answer might work for you.
I wanted to mention the java 9's Matcher.replaceAll lambda extension:
// Find quoted strings and remove there whitespace:
s = Pattern.compile("\"[^\"]*\"").matcher(s)
.replaceAll(mr -> mr.group().replaceAll("\\s", ""));
// Turn the remaining whitespace in a comma and brace all.
s = '{' + s.trim().replaceAll("\\s+", ", ") + '}';
Probably the other answer is better but still I have written it so I will post it here ;) It takes a different approach
public static void main(String[] args) {
String test="abc test=\"x y z\" magic=\" hello \" hola";
Pattern pattern = Pattern.compile("([^\\\"]+=\\\"[^\\\"]+\\\" )");
Matcher matcher = pattern.matcher(test);
int lastIndex=0;
while(matcher.find()) {
String[] parts=matcher.group(0).trim().split("=");
boolean newLine=false;
for (String string : parts[0].split("\\s+")) {
if(newLine)
System.out.println();
newLine=true;
System.out.print(string);
}
System.out.println("="+parts[1].replaceAll("\\s",""));
lastIndex=matcher.end();
}
System.out.println(test.substring(lastIndex).trim());
}
Result is
abc
test="xyz"
magic="hello"
hola
It sounds like you want to write a basic parser/Tokenizer. My bet is that after you make something that can deal with pretty printing in this structure, you will soon want to start validating that there arn't any mis-matching "'s.
But in essence, you have a few stages for this particular problem, and Java has a built in tokenizer that can prove useful.
import java.util.LinkedList;
import java.util.List;
import java.util.StringTokenizer;
import java.util.stream.Collectors;
public class Q50151376{
private static class Whitespace{
Whitespace(){ }
#Override
public String toString() {
return "\n";
}
}
private static class QuotedString {
public final String string;
QuotedString(String string) {
this.string = "\"" + string.trim() + "\"";
}
#Override
public String toString() {
return string;
}
}
public static void main(String[] args) {
String test = "abc test=\"x y z\" magic=\" hello \" hola";
StringTokenizer tokenizer = new StringTokenizer(test, "\"");
boolean inQuotes = false;
List<Object> out = new LinkedList<>();
while (tokenizer.hasMoreTokens()) {
final String token = tokenizer.nextToken();
if (inQuotes) {
out.add(new QuotedString(token));
} else {
out.addAll(TokenizeWhitespace(token));
}
inQuotes = !inQuotes;
}
System.out.println(joinAsStrings(out));
}
private static String joinAsStrings(List<Object> out) {
return out.stream()
.map(Object::toString)
.collect(Collectors.joining());
}
public static List<Object> TokenizeWhitespace(String in){
List<Object> out = new LinkedList<>();
StringTokenizer tokenizer = new StringTokenizer(in, " ", true);
boolean ignoreWhitespace = false;
while (tokenizer.hasMoreTokens()){
String token = tokenizer.nextToken();
boolean whitespace = token.equals(" ");
if(!whitespace){
out.add(token);
ignoreWhitespace = false;
} else if(!ignoreWhitespace) {
out.add(new Whitespace());
ignoreWhitespace = true;
}
}
return out;
}
}
I have a text file and want to tokenize its lines -- but only the sentences with the # character.
For example, given...
Buah... Molt bon concert!! #Postconcert #gintonic
...I want to print only #Postconcert #gintonic.
I have already tried this code with some changes...
public class MyTokenizer {
/**
* #param args
*/
public static void main(String[] args) {
tokenize("Europe3.txt","allo.txt");
}
public static void tokenize(String sFile,String sFileOut) {
String sLine="", sToken="";
MyBufferedReaderWriter f = new MyBufferedReaderWriter();
f.openRFile(sFile);
MyBufferedReaderWriter fOut = new MyBufferedReaderWriter();
fOut.openWFile(sFileOut);
while ((sLine=f.readLine()) != null) {
//StringTokenizer st = new StringTokenizer(sLine, "#");
String[] tokens = sLine.split("\\#");
for (String token : tokens)
{
fOut.writeLine(token);
//System.out.println(token);
}
/*while (st.hasMoreTokens()) {
sToken = st.nextToken();
System.out.println(sToken);
}*/
}
f.closeRFile();
}
}
Can anyone help?
You can try something like with Regex:
package com.stackoverflow.answers;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class HashExtractor {
public static void main(String[] args) {
String strInput = "Buah... Molt bon concert!! #Postconcert #gintonic";
String strPattern = "(?:\\s|\\A)[##]+([A-Za-z0-9-_]+)";
Pattern pattern = Pattern.compile(strPattern);
Matcher matcher = pattern.matcher(strInput);
while (matcher.find()) {
System.out.println(matcher.group());
}
}
}
As per the given example, when using the split() function the values would be stored something like this:
tokens[0]=Buah... Molt bon concert!!
tokens[1]=Postconcert
tokens[2]=gintonic
So you just need to skip first value and append '#' (if you need that in your other) to the other string values.
Hope this helps.
You have not specially asked for this, but I assume you try to extract all the #hashtags from your textfile.
To do this, Regex is your friend:
String text = "Buah... Molt bon concert!! #Postconcert #gintonic";
System.out.println(getHashTags(text));
public Collection<String> getHashTags(String text) {
Pattern pattern = Pattern.compile("(#\\w+)");
Matcher matcher = pattern.matcher(text);
Set<String> htags = new HashSet();
while (matcher.find()) {
htags.add(matcher.group(1));
}
return htags;
}
Compile a pattern like this #\w+, everything that starts with a # followed by one or more (+) word character (\w).
Then we have to escape the \ for java with a \\.
And finally put this expression in a group to get access to the matched text by surrounding it with braces (#\w+).
For every match, add the first matched group to the set htags, finally we get a set with all the hashtags in it.
[#gintonic, #Postconcert]
I have a string test in which I can see VD1 and and VD2.
How can I extract the value of VD1 and VD2 and store it in string.
String test =
"DomainName=xyz.zzz.com
&ModifiedOn=03%2f17%2f2015
&VD1=MTMwMDE3MDQ%3d
&VD2=B67E48F6969E99A0BC2BEE0E240D2B5C
&SiteLanguage=English"
Here value of VD1=MTMwMDE3MDQ%3d and VD2=B67E48F6969E99A0BC2BEE0E240D2B5C. But these are the dynamic values. Here VD1 and VD2 are seperated by '&'.
Try regex like this :
public static void main(String[] args) throws Exception {
String test = "DomainName=xyz.zzz.com&ModifiedOn=03%2f17%2f2015&VD1=MTMwMDE3MDQ%3d&VD2=B67E48F6969E99A0BC2BEE0E240D2B5C&SiteLanguage=English";
Pattern p = Pattern.compile("VD1=(.*)&VD2=(.*)&");
Matcher m = p.matcher(test);
while(m.find()){
System.out.println(m.group(1));
System.out.println(m.group(2));
}
}
O/P :
MTMwMDE3MDQ%3d
B67E48F6969E99A0BC2BEE0E240D2B5C
You can use regular expressions or use the String index() and split() methods.
A regular expression that matches and captures the VD1 value is
/VD1=([^&]*)/
If you're sure that theres always a "&" behind the values of VD1 and VD2, this kind of splitting will do:
String test = "DomainName=xyz.zzz.com&ModifiedOn=03%2f17%2f2015&VD1=MTMwMDE3MDQ%3d&VD2=B67E48F6969E99A0BC2BEE0E240D2B5C&SiteLanguage=English";
String vd1 = test.substring(test.indexOf("VD1=")+4, test.indexOf("&", test.indexOf("VD1")));
String vd2 = test.substring(test.indexOf("VD2=")+4, test.indexOf("&", test.indexOf("VD2")));
System.out.println("VD1:" + vd1 + "\nVD2:" + vd2);
This is only a demo, for production you'd have to extract the indexes for better performance.
You can use String.split(...) to split a String in pieces. For example, test.split("&") first splits the String in individual tokens (of the form "key=value").
You could do the following to achieve this:
String vd1 = null, vd2 = null;
for (String token : test.split("&")) {
// For each token, we check if it is one of the keys we need:
if (token.startsWith("VD1=")) {
// The number 4 represents the length of the String "vd1="
vd1 = token.substring(4);
} else if (token.startsWith("VD2=") {
vd2 = token.substring(4);
}
}
System.out.println("VD1 = " + vd1);
System.out.println("VD2 = " + vd2);
However, if you want to parse arbitrary keys, consider using a more robust solution (instead of hard-coding the keys in the for-loop).
Also see the documentation for the String class
String test = "DomainName=xyz.zzz.com&Test&ModifiedOn=03%2f17%2f2015&VD1=MTMwMDE3MDQ%3d&VD2=B67E48F6969E99A0BC2BEE0E240D2B5C&SiteLanguage=English";
HashMap<String, String> paramsMap = new HashMap<String, String>();
String[] params = test.split("&");
for (int i=0; i<params.length; i++) {
String[] param = params[i].split("=");
String paramName = URLDecoder.decode(param[0], "UTF-8");
String paramValue = null;
if(param.length > 1)
paramValue = URLDecoder.decode(param[1], "UTF-8");
paramsMap.put(paramName, paramValue);
}
String vd1 = paramsMap.get("VD1");
String vd2 = paramsMap.get("VD2");
I have something like:
String text = "The user {0} has email address {1}."
// params = { "Robert", "myemailaddr#gmail.com" }
String msg = MessageFormat.format(text, params);
This isn't great for me, because sometimes my translators are not sure what goes in the {0} and {1}, also it would be nice to be able to reword the messages without worrying about the order of the args.
I'd like to replace the arguments with readable names instead of numbers. Something like this:
String text = "The user {USERNAME} has email address {EMAILADDRESS}."
// Map map = new HashMap( ... [USERNAME="Robert", EMAILADDRESS="myemailaddr#gmail.com"]
String msg = MessageFormat.format(text, map);
Is there an easy way to do this?
Thanks!
rob
You can use MapFormat for this. Find out the details here:
http://www.java2s.com/Code/Java/I18N/AtextformatsimilartoMessageFormatbutusingstringratherthannumerickeys.htm
String text = "The user {name} has email address {email}.";
Map map = new HashMap();
map.put("name", "Robert");
map.put("email", "rhume55#gmail.com");
System.out.println("1st : " + MapFormat.format(text, map));
OUTPUT:
1st : The user Robert has email address rhume55#gmail.com.
See StrSubstitutor from org.apache.commons.lang3:
Map valuesMap = HashMap();
valuesMap.put("animal", "quick brown fox");
valuesMap.put("target", "lazy dog");
String templateString = "The ${animal} jumped over the ${target}.";
StrSubstitutor sub = new StrSubstitutor(valuesMap);
String resolvedString = sub.replace(templateString);
// resolvedString: "The quick brown fox jumped over the lazy dog."
Easy to make one yourself. This is what I use (the main() function is just for test code):
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class StringTemplate {
final private String template;
final private Matcher m;
static final private Pattern keyPattern =
Pattern.compile("\\$\\{([a-zA-Z][a-zA-Z0-9_]*(\\.[a-zA-Z][a-zA-Z0-9_]*)*)\\}");
private boolean blanknull=false;
public StringTemplate(String template) {
this.template=template;
this.m = keyPattern.matcher(template);
}
/**
* #param map substitution map
* #return substituted string
*/
public String substitute(Map<String, ? extends Object> map)
{
this.m.reset();
StringBuffer sb = new StringBuffer();
while (this.m.find())
{
String k0 = this.m.group();
String k = this.m.group(1);
Object vobj = map.get(k);
String v = (vobj == null)
? (this.blanknull ? "" : k0)
: vobj.toString();
this.m.appendReplacement(sb, Matcher.quoteReplacement(v));
}
this.m.appendTail(sb);
return sb.toString();
}
public StringTemplate setBlankNull()
{
this.blanknull=true;
return this;
}
static public void main(String[] args)
{
StringTemplate t1 = new StringTemplate("${this} is a ${test} of the ${foo} bar=${bar} ${emergency.broadcasting.system}");
t1.setBlankNull();
Map<String, String> m = new HashMap<String, String>();
m.put("this", "*This*");
m.put("test", "*TEST*");
m.put("foo", "$$$aaa\\\\111");
m.put("emergency.broadcasting.system", "EBS");
System.out.println(t1.substitute(m));
}
}
Your question is closely related to: How to replace a set of tokens in a Java String
You could use velocity or another template library. But there will be some pain because Java does not have any kind of Map literals.
I know my answer comes a little late, but if you still need this functionality, without the need to download a full-fledged template engine you can take a look at aleph-formatter (I am one of the authors):
Student student = new Student("Andrei", 30, "Male");
String studStr = template("#{id}\tName: #{st.getName}, Age: #{st.getAge}, Gender: #{st.getGender}")
.arg("id", 10)
.arg("st", student)
.format();
System.out.println(studStr);
Or you can chain the arguments:
String result = template("#{x} + #{y} = #{z}")
.args("x", 5, "y", 10, "z", 15)
.format();
System.out.println(result);
// Output: "5 + 10 = 15"
Internally it works using a StringBuilder creating the result by "parsing" the expression, no string concatenation, regex/replace is performed.
static final Pattern REPLACE_PATTERN = Pattern.compile("\\x24\\x7B([a-zA-Z][\\w\\x2E].*?)\\x7D");
/**
* Check for unresolved environment
*
* #param str
* #return origin if all substitutions resolved
*/
public static String checkReplacement(String str) {
Matcher matcher = REPLACE_PATTERN.matcher(str);
if (matcher.find()) {
throw LOG.getIllegalArgumentException("Environment variable '" + matcher.group(1) + "' is not defined");
}
return str;
}
// replace in str ${key} to value
public static String resolveReplacement(String str, Map<String, String> replacements) {
Matcher matcher = REPLACE_PATTERN.matcher(str);
while (matcher.find()) {
String value = replacements.get(matcher.group(1));
if (value != null) {
str = matcher.replaceFirst(replaceWindowsSlash(value));
}
}
return str;
}
But you loose all format options (like ##.#)
I have the following template String: "Hello [Name] Please find attached [Invoice Number] which is due on [Due Date]".
I also have String variables for name, invoice number and due date - what's the best way to replace the tokens in the template with the variables?
(Note that if a variable happens to contain a token it should NOT be replaced).
EDIT
With thanks to #laginimaineb and #alan-moore, here's my 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();
}
I really don't think you need to use a templating engine or anything like that for this. You can use the String.format method, like so:
String template = "Hello %s Please find attached %s which is due on %s";
String message = String.format(template, name, invoiceNumber, dueDate);
The most efficient way would be using a matcher to continually find the expressions and replace them, then append the text to a string builder:
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();
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();
}
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"
You can use Apache Commons StringSubstitutor:
For example:
// Build map
Map<String, String> valuesMap = new HashMap<>();
valuesMap.put("animal", "quick brown fox");
valuesMap.put("target", "lazy dog");
String templateString = "The ${animal} jumped over the ${target}.";
// Build StringSubstitutor
StringSubstitutor sub = new StringSubstitutor(valuesMap);
// Replace
String resolvedString = sub.replace(templateString);
yielding:
"The quick brown fox jumped over the lazy dog."
You can also customize the prefix and suffix delimiters (${ and } respectively in the example above) by using:
setVariablePrefix
setVariableSuffix
You can also specify a default value using syntax like below:
String templateString = "The ${animal:giraffe} jumped over the ${target}.";
which would yield "The giraffe jumped over the lazy dog." when no animal parameter was supplied.
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);
The following replaces variables of the form <<VAR>>, with values looked up from a Map. You can test it online here
For example, with the following input string
BMI=(<<Weight>>/(<<Height>>*<<Height>>)) * 70
Hi there <<Weight>> was here
and the following variable values
Weight, 42
Height, HEIGHT 51
outputs the following
BMI=(42/(HEIGHT 51*HEIGHT 51)) * 70
Hi there 42 was here
Here's the code
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));
}
and although not requested, you can use a similar approach to replace variables in a string with properties from your application.properties file, though this may already be being done:
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;
}
}