I have tried alterations using '|' but seems to be impossible to first parse map if possible, if not, just parse the value as a whole, but keeping the capture group as no 1/2, tried with branch reset group but didn't manage that way either. Any suggestions are welcome.
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
class Scratch {
public static void main(String[] args) {
final Pattern outerKeyPattern = Pattern.compile("([A-Z]+)\\((.*)\\)", Pattern.MULTILINE);
final Pattern innerPattern = Pattern.compile("([A-Z]+)\\((.*?)\\)");
// There will always be a value in the outer,
// but sometimes it's an inner map, i.e only need to solve this case, no other exeptions
String input = """
VALUE(123)
OUTERVALUE(INNERVALUE(123)OTHERVALUE(456))
""";
Map<String, Map<String, String>> outer = new HashMap<>();
Matcher matcher = outerKeyPattern.matcher(input);
while (matcher.find()) {
String key = matcher.group(1);
String value = matcher.group(2);
Matcher valueMatcher = innerPattern.matcher(value);
Map<String, String> innerMap = new HashMap<>();
while (valueMatcher.find()) {
innerMap.put(valueMatcher.group(1), valueMatcher.group(2));
}
outer.put(key, innerMap);
}
System.out.println(outer);
}
}
yields output:
{VALUE={}, OUTERVALUE={INNERVALUE=123, OTHERVALUE=456}}
I need to parse value also as key for the inner map and it's value as null:
{VALUE={123=}, OUTERVALUE={INNERVALUE=123, OTHERVALUE=456}}
Since you seem to process your data line by line and the comment in your snippet indicates that your data is well formed, you could first extract the whole content of the outter braces and then check if the value contains any nested key-value pairs. With some modifications to your regex, something like below might be what you are looking for:
public static void main(final String[] args) {
Pattern outerPattern = Pattern.compile("([A-Z]+)\\((.*)\\)");
Pattern innerPattern = Pattern.compile("([A-Z]+)\\(([^()]*)\\)");
String input = """
VALUE(123)
OUTERVALUE(INNERVALUE(123)OTHERVALUE(456))
""";
Map<String, Map<String, String>> outer = new HashMap<>();
input.lines().forEach(line -> {
Matcher matcher = outerPattern.matcher(line);
matcher.find();
String key = matcher.group(1);
String value = matcher.group(2);
Map<String, String> innerMap = new HashMap<>();
if (!innerPattern.asPredicate().test(value)) { //check if value doesn't contain any nested key-value-pairs
innerMap.put(value, "");
} else {
Matcher valueMatcher = innerPattern.matcher(value);
while (valueMatcher.find()) {
innerMap.put(valueMatcher.group(1), valueMatcher.group(2));
}
}
outer.put(key, innerMap);
});
System.out.println(outer);
}
Related
this code used for check if word in string in hashMap key
String[] arrs = message.split("(?<! ) |(?<= {2})");
for(int j = 0 ; j < arrs.length; j++){
if(AppConfig.hashMap.containsKey(arrs[j])){
int s = AppConfig.hashMap.get(arrs[j]);
} else
text.append(" "+arrs[j]);
}
and the hashMap its
public static Map<String, Integer> hashMap = new HashMap<String, Integer>()
{{
put(":)", R.drawable.emoji_1f60a_64);
put(":D", R.drawable.emoji_1f601_64);
put(":'(", R.drawable.emoji_1f622_64);
put(":P", R.drawable.emoji_1f61c_64);
put(";)", R.drawable.emoji_1f609_64);
put(":O", R.drawable.emoji_1f632_64);
put("-_-", R.drawable.emoji_1f620_64);
put(":*", R.drawable.emoji_1f618_64);
put("<3", R.drawable.emoji_2764_64);
put("^_^", R.drawable.emoji_2764_64);
}};
now its can replace :) with drawable emogi but the problem when i use another smile symbole
when i loop on string and compare every word if found in hashMap
if(AppConfig.hashMap.containsKey(arrs[j])) //found smile replace with emogi
its check if there are :) or :D in string but problem when there are smiles symbols like those
"😌","😃","😄","😞","😢","😷","😓","😰"
so the hashMap will be
public static Map<String, Integer> hashMap = new HashMap<String, Integer>()
{{
put("😌", R.drawable.emoji_1f60a_64);
put("😃", R.drawable.emoji_1f601_64);
put("😄", R.drawable.emoji_1f622_64);
put("😞", R.drawable.emoji_1f61c_64);
put("😢", R.drawable.emoji_1f609_64);
put("😷", R.drawable.emoji_1f632_64);
put("😓", R.drawable.emoji_1f620_64);
put("😰", R.drawable.emoji_1f618_64);
}};
here i have this string
hi how are you ? 😌 😃 😄
now when check if there are key in hashMap equal 😌 or 😃 ...
by
if(AppConfig.hashMap.containsKey(arrs[j]))
its fail and say no key with this string
You could create a dynamic pattern from the values in the hashmap (and reuse it because it's expensive to create a pattern it for every execution).
Take a look at https://stackoverflow.com/a/1326962/6709113
For example:
package aa;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.lang3.StringUtils;
public class Test {
public static String replaceMultipleStrings(String message) {
Map<String,String> tokens = new HashMap<String,String>();
tokens.put("Garfield", "--Garfield--");
tokens.put("😌", "--😌--");
tokens.put("😃", "--😃--");
tokens.put("😄", "--😄--");
// Create pattern of the format "(cat|beverage)"
String patternString = "(" + StringUtils.join(tokens.keySet(), "|") + ")";
Pattern pattern = Pattern.compile(patternString);
Matcher matcher = pattern.matcher(message);
StringBuffer sb = new StringBuffer();
while(matcher.find()) {
matcher.appendReplacement(sb, tokens.get(matcher.group(1)));
}
matcher.appendTail(sb);
String result = sb.toString();
System.out.println(result);
return result;
}
public static void main(String[] args)
{
replaceMultipleStrings("hi how are you, my friend Garfield 😌 😃 😄?");
}
}
The result is: "hi how are you, my friend --Garfield-- --😌-- --😃-- --😄--?"
If performance is required, the HashMap and Pattern creation should be done only once.
I am having a string template containing $variables which needs to be replaced.
String Template: "hi my name is $name.\nI am $age old. I am $sex"
The solution which i tried verifying does not work in the java program.
http://regexr.com/3dtq1
Further, I referred to https://www.regex101.com/ where i could not check if the pattern works for java. But, while going through one of the tutorials I found that "$ Matches end of line". what's the best way to replace the tokens in the template with the variables?
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class PatternCompiler {
static String text = "hi my name is $name.\nI am $age old. I am $sex";
static Map<String,String> replacements = new HashMap<String,String>();
static Pattern pattern = Pattern.compile("\\$\\w+");
static Matcher matcher = pattern.matcher(text);
public static void main(String[] args) {
replacements.put("name", "kumar");
replacements.put("age", "26");
replacements.put("sex", "male");
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);
System.out.println(buffer.toString());
}
}
You are using matcher.group(1) but you didn't define any group in the regexp (( )), so you can use only group() for the whole matched string, which is what you want.
Replace line:
String replacement = replacements.get(matcher.group(1));
With:
String replacement = replacements.get(matcher.group().substring(1));
Notice the substring, your map contains only words, but matcher will match also $, so you need to search in map for "$age".substring(1)" but do replacement on the whole $age.
You can try replacing the pattern string with
\\$(\\w+)
and the variable replacement works. Your current pattern only has group 0 (the entire pattern) but not group 1. Adding the parenthesis makes the first group the variable name and the replacement will replace the dollar sign and the variable name.
Your code has just minor glitches.
static Map<String,String> replacements = new HashMap<>();
static Pattern pattern = Pattern.compile("\\$\\w+\\b"); // \b not really needed
// As no braces (...) there is no group(1)
String replacement = replacements.get(matcher.group());
Your not using the right thing as your key. Change to group(), and change map to '$name' etc:
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class HelloWorld {
static String text = "hi my name is $name.\nI am $age old. I am $sex";
static Map<String,String> replacements = new HashMap<String,String>();
static Pattern pattern = Pattern.compile("\\$\\w+");
static Matcher matcher = pattern.matcher(text);
public static void main(String[] args) {
replacements.put("$name", "kumar");
replacements.put("$age", "26");
replacements.put("$sex", "male");
StringBuffer buffer = new StringBuffer();
while (matcher.find()) {
String replacement = replacements.get(matcher.group());
System.out.println(replacement);
if (replacement != null) {
// matcher.appendReplacement(buffer, replacement);
// see comment
matcher.appendReplacement(buffer, "");
buffer.append(replacement);
}
}
matcher.appendTail(buffer);
System.out.println(buffer.toString());
}
}
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.
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 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 ##.#)