Java string replace regex pattern with evaluated key - java

I have an input string like this
I want to go to {places} where {things} are happening.
The values for {places} and {things} are computed lazily (i.e. first i find out what all keys needs to be replaced and then compute the values for them, and then replace them in the original string).
I am able to find out all the keys and removing them using the below code.
public class Temp {
private static final Pattern betweenCurlyBracesMatcher = Pattern.compile("\\{(.*?)\\}");
public static void main(String args[]) {
System.out.println(resolve2("hello {world} from {here}"));
}
public static String resolve2(String input) {
Map<String, String> keyValueMap = new HashMap<>();
Matcher matcher = betweenCurlyBracesMatcher.matcher(input);
while (matcher.find()) {
String key = matcher.group(1);
if (!keyValueMap.containsKey(key)) {
keyValueMap.put(key, computeValueForKey(key));
}
}
for (Map.Entry<String, String> entry : keyValueMap.entrySet()) {
input = input.replace("{" + entry.getKey() + "}", entry.getValue()); // << ugly code here
}
return input;
}
private static String computeValueForKey(String key) {
return "new" + key;
}
}
but I am not happy with
input = input.replace("{" + entry.getKey() + "}", entry.getValue());
because it means whenever i change my regex i will have to update this logic. Is there a more elegant solution to this problem.
input hello {world} from {here}
output hello newworld from newhere
input I want to go to {places} where {things} are happening.
output I want to go to newplaces where newthings are happening.

You should make use of the Matcher#appendReplacement and Matcher#appendTail APIs here:
Map<String, String> keyValueMap = new HashMap<>();
keyValueMap.put("places", "to America");
keyValueMap.put("things", "events");
String input = "I want to go to {places} where {things} are happening.";
Pattern pattern = Pattern.compile("\\{(.*?)\\}");
Matcher matcher = pattern.matcher(input);
StringBuffer buffer = new StringBuffer();
while(matcher.find()) {
matcher.appendReplacement(buffer, keyValueMap.get(matcher.group(1)));
}
matcher.appendTail(buffer);
System.out.println(buffer.toString());
This prints:
I want to go to to America where events are happening.

We can use matcher.group(0) with returns entire matched string
public class Temp {
private static final Pattern betweenCurlyBracesMatcher = Pattern.compile("\\{(.*?)\\}");
public static void main(String args[]) {
System.out.println(resolve2("hello {world} from {here}"));
}
public static String resolve2(String input) {
Map<String, String> keyValueMap = new HashMap<>();
Matcher matcher = betweenCurlyBracesMatcher.matcher(input);
while (matcher.find()) {
String keyBetweenBraces = matcher.group(1);
String keyWithBraces = matcher.group(0);
if (!keyValueMap.containsKey(keyWithBraces)) {
keyValueMap.put(keyWithBraces, computeValueForKey(keyBetweenBraces));
}
}
for (Map.Entry<String, String> entry : keyValueMap.entrySet()) {
input = input.replace(entry.getKey(), entry.getValue());
}
return input;
}
private static String computeValueForKey(String key) {
return "new" + key;
}
}

Related

Splitting a string for last occurrence a character

I'm trying to split a String with a format like this :
"abc=cde,dfe=lk,f,sss=f,d,s"
I'd like to recover these values in a map by using the first set of characters as a key and the second ones as value.
For example
key: abc, value: cde
key: dfe, value: lk,f
key: sss, value: f,d,s
So splitting these values for the last occurrence of ",".
Any ideas on how to do it?
I tried with regex and Stringtokenizer but I can't manage to recover just the last occurrence of ","
You could use the following regex (could possibly be optimized):
,(?=(?:(?!,).)*=)
(see on Regex101)
This matches a , which has no subsequent , until the next =.
You need to use regex for this.
Full Code :
public class Test {
public static void main(String args[]) {
String input = "abc=cde,dfe=lk,f,sss=f,d,s";
String[] arrOfStr = input.split(",(?=(?:(?!,).)*=)");
HashMap<String, String> properties = new HashMap<String, String>();
for(int i=0;i<arrOfStr.length;i++) {
String[] temp = arrOfStr[i].split("=");
properties.put(temp[0], temp[1]);
}
System.out.println("Input String : " +input);
System.out.println("\nFinal properties : ");
properties.entrySet().forEach(entry->{
System.out.println("key = " +entry.getKey() + " :: value = " + entry.getValue());
});
}
}
Output :
Input String : abc=cde,dfe=lk,f,sss=f,d,s
Final properties :
key = dfe :: value = lk,f
key = sss :: value = f,d,s
key = abc :: value = cde
Full Code :
public class Test {
public static void main(String args[]) {
String text = "abc=cde,dfe=lk,f,sss=f,d,s";
String[] parts = text.split(",");
Map<String, String> map = new LinkedHashMap<>();
String key = null;
StringBuilder value = new StringBuilder();
for (int i = 0; i < parts.length; i++) {
if (parts[i].contains("=")) {
if (key != null) {
map.put(key, value.toString());
value.setLength(0);
}
String[] innerParts = parts[i].split("=");
key = innerParts[0];
value.append(innerParts[1]);
} else {
value.append(',').append(parts[i]);
}
}
map.put(key, value.toString());
for (Map.Entry<String, String> entry : map.entrySet()) {
System.out.println(entry);
}
}
}
Output :
abc=cde
dfe=lk,f
sss=f,d,s

REGEX to replace numbers with String

I have a String as below:
((((1 OR 2) AND 3) OR 11) AND 23)
I want to replace numbers with String values.
I am new to regex and unable to figure out how to replace numbers lying within a range.
Here is my code to resolve your problem. My poor regex knowlodge was not enough to resolve by only using regex.
public static void main(String[] args)
{
String temp = "(X)+|";
String regex = "";
String text = "((((1 OR 2) AND 3) OR 11) AND 23)";
Map<String, String> numberToString = new TreeMap<>((o1, o2) -> Integer.valueOf(o2) - Integer.valueOf(o1));
numberToString.put("3", "THREE");
numberToString.put("2", "TWO");
numberToString.put("1", "ONE");
numberToString.put("11", "ELEVEN");
numberToString.put("23", "TWENTYTHREE");
for(String number : numberToString.keySet()){
regex = regex + temp.replace("X", number);
}
regex = regex.substring(0, regex.lastIndexOf('|'));
Set<String> allMatches = new TreeSet<>((o1, o2) -> Integer.valueOf(o2) - Integer.valueOf(o1));
Matcher m = Pattern.compile(regex).matcher(text);
while (m.find()) {
allMatches.add(m.group());
}
for (String match : allMatches) {
if (numberToString.get(match) != null)
text = text.replaceAll(match, numberToString.get(match));
}
System.out.println(text);
System.out.println(regex);
}
Use str.replaceAll(1, "one"); function

How can i make an ID that adds Every time but doesnt Remove Zeros(0)

hey guys i have this id ABCD000000001XYZL
public class SmartCounter {
public String SmartCounter(String strID){
intNumber =Integer.parseInt(strID.replaceAll("\\D+",""));
intNumber += 1;
strReturn = "ABCD"+intNumber+"XYZL";
(strReturn);
}
}
Im just asking how can i add 1 to the Number part and return it into a string without loosing the zeros? TIA :D
Assuming the id is of the format
4 letters
9 numbers
4 letters
You can parse it using Regex, increment the number, then rebuild it. Something like this:
public static void main(String[] args) throws Exception {
final Pattern pattern = Pattern.compile("(\\D{4})(\\d{9})(\\D{4})");
final String input = "ABCD000000001XYZL";
final Matcher matcher = pattern.matcher(input);
if (matcher.matches()) {
final String head = matcher.group(1);
final long number = Long.parseLong(matcher.group(2)) + 1;
final String tail = matcher.group(3);
final String result = String.format("%s%09d%s", head, number, tail);
System.out.println(result);
}
}
You can use snazzier Regex and the Matcher.appendReplacement to make the code a little shorter; at the cost of complexity:
public static void main(String[] args) throws Exception {
final Pattern pattern = Pattern.compile("(?<=\\D{4})(\\d{9})(?=\\D{4})");
final String input = "ABCD000000001XYZL";
final Matcher matcher = pattern.matcher(input);
final StringBuffer result = new StringBuffer();
if (matcher.find()) {
final long number = Long.parseLong(matcher.group(1)) + 1;
matcher.appendReplacement(result, String.format("%09d", number));
}
matcher.appendTail(result);
System.out.println(result);
}
Have a look at DecimalFormat I'm sure you can figure out the details on your own
You could use String.format(String, Object...) and something like this,
class SmartCounter {
private int id = 1;
public SmartCounter() {
this.id = 1;
}
public SmartCounter(int id) {
this.id = id;
}
public String smartCounter() {
return String.format("ABCD%09dXYZL", id++);
}
}
Which you can run like
public static void main(String[] args) {
SmartCounter sc = new SmartCounter();
for (int i = 0; i < 3; i++) {
System.out.println(sc.smartCounter());
}
}
Output is
ABCD000000001XYZL
ABCD000000002XYZL
ABCD000000003XYZL

Java Pattern/Matcher - Return the matches from one method to another

I'm an absolute Java beginner. I've searched on the forums, but couldn't find an answer to this question.
I have two classes, one which browses through an arraylist of sentences. I only attach the for-each loop as seen below.
"matching" is the instance of the other class (containing the pattern/matcher code)
matchEndings is the method, attached below.
for (String sentence: sentences) {
String match = matching.matchEndings(sentence);
if (match.length() > 0) {
System.out.println(match);
}
}
This is the method.
public String matchEndings(String s){
Pattern p = Pattern.compile(".*?(aa|ee)");
Matcher m = p.matcher(s);
return m.group();
}
My question is, how do I return the matched sentences, containing aa / ee endings, to the first class, and have it printed there? The code is compiled, but when I run I get
Exception in thread "main" java.lang.IllegalStateException: No match found
at java.util.regex.Matcher.group(Unknown Source)
at java.util.regex.Matcher.group(Unknown Source)
Thank you so much in advance!
Matcher.group() only returns if there is a match already. You need to do something like this:-
if (m.matches()) {
return m.group();
} else {
return "";
}
It seems like overkill to use RegEx when all you need is a simple endsWith(String):
public void print(final List<String> sentences, final String... endings){
for(final String sentence : sentences){
for(final String ending : endings){
if(sentence.endsWith(ending)){
System.out.println(sentence);
break;
}
}
}
}
The method above will loop through a List<String> of sentences and print out all of the sentences that end with one of the elements in endings. For usage, you could try:
print(sentences, "aa", "ee");
Where sentences is your ArrayList<String> of sentences.
The matches or find methods must precede the group method. Since matches attempts to match the entire region against the pattern, it is more appropriate here
public String matchEndings(String s){
Pattern p = Pattern.compile("(aa|ee)$");
Matcher m = p.matcher(s);
if (m.matches) {
return m.group();
} else {
return ""
}
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class PotenssienSumma {
public static void main(String[] args) {
ArrayList<String> sentences = new ArrayList<>(10);
sentences.add("aa");
sentences.add("1324");
for (String sentence: sentences) {
String match = Matching.matchEndings(sentence);
if (match.length() > 0) {
System.out.println(match);
}
}
}
}
class Matching{
public static String matchEndings(String s){
Pattern p = Pattern.compile(".*?(aa|ee)");
Matcher m = p.matcher(s);
if (m.matches()) {
return m.group();
} else {
return "";
}
}
}
Don't construct Pattern in the method. That is expensive. Put the pattern object into a static final variable.
The correct use for patterns is the following:
while(matcher.find()) {
sysout(matcher.group());
}
This will print all matches, if you want just one match, replace the while with if.
I don't know if this is intentional but your regex doesn't match ee|aa to end of the string. It matches ee or aa anywhere in the string along with any characters preceeding it. For instance for string Fox preens in front of a vixen your regex returns string Fox pree. Don't know if that's intended.
Here's a class that takes a list or a collection of strings as an argument and then lazily finds all words that end in aa or ee. It has a main method you can run to test.
public class Endings implements Iterable<String> {
private final Iterable<String> strings;
private static final Pattern pat = Pattern.compile("(?<=^|\\s)\\S*(aa|ee)(?=\\s|$)");
public static void main(String[] args) {
Endings endings = new Endings(Arrays.asList("This testaabb testee testaa", "Test2aa Test3ee ", "no match"));
for(String word : endings) {
System.out.println(word);
}
}
public Endings(Iterable<String> strings) {
this.strings = strings;
}
public Iterator<String> iterator() {
return new Iterator<String>() {
private Iterator<String> iter = strings.iterator();
private Matcher m;
private String result;
public boolean hasNext() {
if (result == null) {
if (m == null) {
if (iter.hasNext()) {
m = pat.matcher(iter.next());
} else {
return false;
}
}
if (m.find()) {
result = m.group();
return true;
} else {
m = null;
return hasNext();
}
} else {
return true;
}
}
public String next() {
if (result != null) {
String ret = result;
result = null;
return ret;
} else {
throw new NoSuchElementException();
}
}
public void remove() {
}
};
}
}

ReplaceFirst with Regular Expression

Let's say I have a String
String link = "www.thisisalink.com/tick1=#tick1#&tick2=#tick2#&tick3=#tick3#&tick4=#tick4#";
Then I can use
link = replaceFirst("(.+)=#\\1#", "");
To make it
link = "www.thisisalink.com/&tick2=#tick2#&tick3=#tick3#&tick4=#tick4#";
But I want to loop though the String, to get what has been replace and save it somewhere else, like a linked list or an array... result would be:
String[] result = ["tick1=#tick1#", "tick2=#tick2#", "tick3=#tick3#", "tick4=#tick4#"];
String link = "www.thisisalink.com/&&&";
But how can I do this? I tried looping with
while (link.matches("(.+)=#\\1#")){}
Which didn't work.
You can use Pattern Matcher classes to iterate over your string to find substrings that will match your regex. Then to replace founded substring you can use appednReplacement and appendTail. To get founded match you can use group() from Matcher instance.
Here is something similar to what you want
String link = "www.thisisalink.com/tick1=#tick1#&tick2=#tick2#&tick3=#tick3#&tick4=#tick4#";
StringBuffer sb = new StringBuffer();
Pattern p = Pattern.compile("(.+)=#\\1#");
Matcher m = p.matcher(link);
List<String> replaced = new ArrayList<>();
while (m.find()) {
m.appendReplacement(sb, "");
replaced.add(m.group());
}
m.appendTail(sb);
//to replace link with String stored in sb use link=sb.toString();
//otherwise link will be unchanged
System.out.println(sb);
System.out.println(replaced);
output:
www.thisisalink.com/&&&
[tick1=#tick1#, tick2=#tick2#, tick3=#tick3#, tick4=#tick4#]
This produces the Strings you want:
public static void main(String[] args)
{
final String link = "www.thisisalink.com/tick1=#tick1#&tick2=#tick2#&tick3=#tick3#&tick4=#tick4#";
final int index = link.indexOf("/") + 1;
final String[] result = link.substring(index).split("&");
final String newLink = link.substring(0, index) + repeat("&", result.length -1);
System.out.println(newLink);
for(final String tick : result)
{
System.out.println(tick);
}
}
private static String repeat(final String toRepeat, final int repetitions)
{
final StringBuilder sb = new StringBuilder(repetitions);
for(int i = 0; i < repetitions; i++)
{
sb.append(toRepeat);
}
return sb.toString();
}
Produces:
www.thisisalink.com/&&&
tick1=#tick1#
tick2=#tick2#
tick3=#tick3#
tick4=#tick4#

Categories