I'm using regex to replace placeholders in a template file.
I have this method:
public static String processTemplate(String template, Map<String, String> attributes) {
Matcher m = PLACEHOLDER_PATTERN.matcher(template);
String message = template;
boolean matches = m.matches();
if (matches) {
for (int i = 1; i < m.groupCount() + 1; i++) {
message = message.replaceAll(m.group(i), attributes.get(m.group(i)));
}
}
return message;
}
with this pattern:
private static final Pattern PLACEHOLDER_PATTERN = Pattern.compile("(\\$\\{.*?})");
But this test fails:
#Test
public void templates() {
Map<String, String> attributes = new HashMap<>();
attributes.put("${wobble}", "wobble");
String result = processTemplate("wibble ${wobble}", attributes);
assertEquals("wibble wobble", result);
}
And I don't know why. It seems that the 'match' is returning false.
This is how you can process your regex:
private static final Pattern PLACEHOLDER_PATTERN = Pattern.compile("\\$\\{.*?}");
public static String processTemplate(String template, Map<String, String> attributes) {
Matcher m = PLACEHOLDER_PATTERN.matcher(template);
StringBuffer sb = new StringBuffer();
while (m.find()) {
if (attributes.containsKey(m.group()))
m.appendReplacement(sb, attributes.get(m.group()));
}
m.appendTail(sb);
return sb.toString();
}
Then call it as:
Map<String, String> attributes = new HashMap<>();
attributes.put("${wobble}", "wobble");
String result = processTemplate("wibble ${wobble}", attributes);
//=> "wibble wobble"
Changes are:
Use matcher.find() instead matcher.matches()
Use matcher.appendReplacement() to append each replacement into a buffer
Finally call matcher.appendTail() to append remaining text
The problem is that you are using Matcher.matches() which, as the docs say:
Attempts to match the entire region against the pattern.
So when you pass in "wibble ${wobble}" the match fails because the "wibble " bit isn't accounted for in your regex.
Instead of Matcher.matches() you should use Matcher.find() which will find the next partial match.
Related
I want to replace all occurrences of particular string with different UUID's. For example,
content = content.replaceAll("xyz", "xyz" + generateUUID());
but problem here is that all the "xyz"'s will get replaced by same UUID. But I want that each "xyz" gets replaced by an individual unique ID. How can this be done?
You can do this using Matcher.appendReplacement. This will give you the replaceAll functionality of a complete regex (not just a static String). Here, I use uidCounter as a very simple generateUUID; you should be able to adapt this to your own generateUUID function.
public class AppendReplacementExample {
public static void main(String[] args) throws Exception {
int uidCounter = 1000;
Pattern p = Pattern.compile("xyz");
String test = "abc xyz def xyz ghi xyz";
Matcher m = p.matcher(test);
StringBuffer sb = new StringBuffer();
while(m.find()) {
m.appendReplacement(sb, m.group() + uidCounter);
uidCounter++;
}
m.appendTail(sb);
System.out.println(sb.toString());
}
}
Output:
abc xyz1000 def xyz1001 ghi xyz1002
You could use a StringBuilder (for efficiency, since String is immutable), a while loop and something like
// content = content.replaceAll("xyz", "xyz" + generateUUID());
StringBuilder sb = new StringBuilder(content);
String toReplace = "xyz";
int toReplaceLen = toReplace.length();
int pos;
while ((pos = sb.indexOf(toReplace)) > -1) {
sb.replace(pos, pos + toReplaceLen, generateUUID());
}
// content = sb.toString(); // <-- if you want to use content.
It looks like you'd like a way to say something like this:
content = content.replaceAll("xyz", x -> x + generateUUID());
Here's an adaptation of durron597's answer that lets you do almost that:
content = replaceAll(content, "xyz", x -> x + generateUUID());
public static String replaceAll(String source, String regex,
Function<String, String> replacement) {
StringBuffer sb = new StringBuffer();
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(source);
while (matcher.find()) {
matcher.appendReplacement(sb, replacement.apply(matcher.group(0)));
}
matcher.appendTail(sb);
return sb.toString();
}
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 the following scenario in my current Java project:
A properties file:
animal1=cat
animal2=dog
A Java method:
public String replace(String input) {
return input.replaceAll("%(.*?)%", properties.getProperty("$1"));
}
The part that says properties.getProperty("$1") obviously doesn't work because it will return the property for the key "$1" but not for the actual value for $1.
Is there any simple method to replace for example "%animal1%" with "cat"?
The properties file will contain a few hundred entries, so searching after a substring that could be replaced for every value in the properties file is not an option.
Don't try to do it as oneliner. If you use a loop to check for all the patterns that might match
Here's some code that will do the trick for you (this should compile and run as-is)
package org.test.stackoverflow;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class PatternReplacer {
private final Pattern keyPattern = Pattern.compile("%([^%]*)%");
private final Properties properties;
public PatternReplacer(Properties propertySeed) {
properties = propertySeed;
}
public String replace(String input) {
int start = 0;
while(true) {
Matcher match = keyPattern.matcher(input);
if(!match.find(start)) break;
String group = match.group(1);
if(properties.containsKey(group)) {
input = input.replaceAll("%" + group + "%", properties.getProperty(group));
} else {
start = match.start() + group.length();
}
}
return input;
}
public static void main(String... args) {
Properties p = new Properties();
p.put("animal1", "cat");
p.put("animal2", "dog");
PatternReplacer test = new PatternReplacer(p);
String result = test.replace("foo %animal1% %bar% %animal2%baz %animal1% qu%ux");
System.out.println(result);
}
}
Output:
foo cat %bar% dogbaz cat qu%ux
If I understand you correctly you will need to use manually use appendReplacement and appendTail methods from Matcher class. This will allow you to pass result of properties.getProperty(matcher.group(1))
Here is basic example of how you can use it. In this example I'm searching for some keyword like string or secret to replace them. Replacement is decided dynamically based on mapping like
string->foo,
secret->whatever
and is determined by simply calling get(keyword) from Map which stores this mapping.
String data = "some string with some secret data";
Map<String,String> properties = new HashMap<>();
properties.put("string", "foo");
properties.put("secret", "whatever");
Pattern p = Pattern.compile("string|secret");
Matcher m = p.matcher(data);
StringBuffer sb = new StringBuffer();
while (m.find()){
m.appendReplacement(sb, properties.get(m.group()));//replace found match
//with result based on group
}
m.appendTail(sb);//append rest of text after last match, in our case " data"
String result = sb.toString();
System.out.println("Original: " + data);
System.out.println("Replaced: " + result);
Result:
Original: some string with some secret data
Replaced: some foo with some whatever data
I have following 2 urls:
https://docs.google.com/a/abc.com/spreadsheet/ccc?key=0Aj9Oa8x5fqsL678FNhOUF0ZEN5b25iVVZNdjdUQm9mM1E&usp=drive_web#gid=0
https://docs.google.com/a/abc.com/file/d/0Aj9Oa8x5fqsL678FNhOUF0ZEN5b25iVVZNdjdUQm9mM1E/edit
I am using following regex:
Pattern.compile(".*key=|/d/(.[^&/])")
as a result of it I want that the matcher.group() returns both urls upto fileId(0Aj9Oa8x5fqsL678FNhOUF0ZEN5b25iVVZNdjdUQm9mM1E) part and matcher.group(1) returns the fileId.
but I am not getting these results.
you fell victim to the precedence rules in regex expressions and forgot the repetition specifier for your character class. try
Pattern.compile("(key=|/d/)([^&/]+)")
your result will be in $2.
If you don't need to use a regex, then use URI:
private static final Pattern PARAM_SEPARATOR = Pattern.compile("&");
private static final Pattern PATH_MATCHER = Pattern.compile("/file/d/([^/]+)");
// In query parameter...
public static String getKeyQueryParamFromURI(final String input)
{
final URI uri = URI.create(input);
final String params = uri.getQuery();
if (params == null)
return null;
for (final String param: PARAM_SEPARATOR.split(input))
if (param.startsWith("key="))
return param.substring(4);
return null;
}
// In path...
public static String getPathMatcherFromURI(final String input)
{
final URI uri = URI.create(input);
final String path = uri.getPath();
if (path == null)
return null;
final Matcher m = PATH_MATCHER.matcher(input);
return m.find() ? m.group(1) : null;
}
Note that unlike a regex, you will receive the result unescaped. If for instance the URI reads key=a%20b, this will return you "a b"!
If you insist on using a regex (why?), then do that instead for the query parameter:
private static final Pattern PATTERN = Pattern.compile("(?<=[?&])key=([^&]+)");
public static String getKeyQueryParamFromURI(final String input)
{
final Matcher m = PATTERN.matcher(input);
return m.find() ? m.group(1) : null;
}
But you'll have to unescape the parameter value yourself...
It's prefer for two different regex pattern to split the regex statement and not use |(OR).
With using different pattern you will have the first capture group the result you wanted.
Pattern1:
.*key=(.*)=.*
Pattern2:
.*\/file\/?\/(.*)\/.*
I am trying to find environment variables in input and replace them with values.
The pattern of env variable is ${\\.}
Pattern myPattern = Pattern.compile( "(${\\.})" );
String line ="${env1}sojods${env2}${env3}";
How can I replace env1 with 1 and env2 with 2 and env3 with 3, so
that after this I will have a new string 1sojods23?
Strings in Java are immutable, which makes this somewhat tricky if you are talking about an arbitrary number of things you need to find and replace.
Specifically you need to define your replacements in a Map, use a StringBuilder (before Java 9, less performant StringBuffer should have been used) and the appendReplacements() and appendTail() methods from Matcher. The final result will be stored in your StringBuilder (or StringBuffer).
Map<String, String> replacements = new HashMap<String, String>() {{
put("${env1}", "1");
put("${env2}", "2");
put("${env3}", "3");
}};
String line ="${env1}sojods${env2}${env3}";
String rx = "(\\$\\{[^}]+\\})";
StringBuilder sb = new StringBuilder(); //use StringBuffer before Java 9
Pattern p = Pattern.compile(rx);
Matcher m = p.matcher(line);
while (m.find())
{
// Avoids throwing a NullPointerException in the case that you
// Don't have a replacement defined in the map for the match
String repString = replacements.get(m.group(1));
if (repString != null)
m.appendReplacement(sb, repString);
}
m.appendTail(sb);
System.out.println(sb.toString());
Output:
1sojods23
I know this is old, I was myself looking for a, appendReplacement/appendTail example when I found it; However, the OP's question doesn't need those complicated multi-line solutions I saw here.
In this exact case, when the string to replace holds itself the value we want to replace with, then this could be done easily with replaceAll:
String line ="${env1}sojods${env2}${env3}";
System.out.println( line.replaceAll("\\$\\{env([0-9]+)\\}", "$1") );
// Output => 1sojods23
DEMO
When the replacement is random based on some conditions or logic on each match, then you can use appendReplacement/appendTail for example
Hopefully you would find this code useful:
Pattern phone = Pattern.compile("\\$\\{env([0-9]+)\\}");
String line ="${env1}sojods${env2}${env3}";
Matcher action = phone.matcher(line);
StringBuffer sb = new StringBuffer(line.length());
while (action.find()) {
String text = action.group(1);
action.appendReplacement(sb, Matcher.quoteReplacement(text));
}
action.appendTail(sb);
System.out.println(sb.toString());
The output is the expected: 1sojods23.
This gives you 1sojods23:
String s = "${env1}sojods${env2}${env3}";
final Pattern myPattern = Pattern.compile("\\$\\{[^\\}]*\\}");
Matcher m = myPattern.matcher(s);
int i = 0;
while (m.find()) {
s = m.replaceFirst(String.valueOf(++i));
m = myPattern.matcher(s);
}
System.out.println(s);
and this works too:
final String re = "\\$\\{[^\\}]*\\}";
String s = "${env1}sojods${env2}${env3}";
int i = 0;
String t;
while (true) {
t = s.replaceFirst(re, String.valueOf(++i));
if (s.equals(t)) {
break;
} else {
s = t;
}
}
System.out.println(s);
You can use a StringBuffer in combination with the Matcher appendReplacement() method, but if the the pattern does not match, there is no point in creating the StringBuffer.
For example, here is a pattern that matches ${...}. Group 1 is the contents between the braces.
static Pattern rxTemplate = Pattern.compile("\\$\\{([^}\\s]+)\\}");
And here is sample function that uses that pattern.
private static String replaceTemplateString(String text) {
StringBuffer sb = null;
Matcher m = rxTemplate.matcher(text);
while (m.find()) {
String t = m.group(1);
t = t.toUpperCase(); // LOOKUP YOUR REPLACEMENT HERE
if (sb == null) {
sb = new StringBuffer(text.length());
}
m.appendReplacement(sb, t);
}
if (sb == null) {
return text;
} else {
m.appendTail(sb);
return sb.toString();
}
}
Map<String, String> replacements = new HashMap<String, String>() {
{
put("env1", "1");
put("env2", "2");
put("env3", "3");
}
};
String line = "${env1}sojods${env2}${env3}";
String rx = "\\$\\{(.*?)\\}";
StringBuffer sb = new StringBuffer();
Pattern p = Pattern.compile(rx);
Matcher m = p.matcher(line);
while (m.find()) {
// Avoids throwing a NullPointerException in the case that you
// Don't have a replacement defined in the map for the match
String repString = replacements.get(m.group(1));
if (repString != null)
m.appendReplacement(sb, repString);
}
m.appendTail(sb);
System.out.println(sb.toString());
In the above example we can use map with just key and values --keys can be env1 ,env2 ..
Use groups once it is matched ${env1} will be your first group and then you use regex to replace what is in each group.
Pattern p = Pattern.compile("(${\\.})");
Matcher m = p.matcher(line);
while (m.find())
for (int j = 0; j <= m.groupCount(); j++)
//here you do replacement - check on the net how to do it;)