I have some strings with equations in the following format ((a+b)/(c+(d*e))).
I also have a text file that contains the names of each variable, e.g.:
a velocity
b distance
c time
etc...
What would be the best way for me to write code so that it plugs in velocity everywhere a occurs, and distance for b, and so on?
Don't use String#replaceAll in this case if there is slight chance part you will replace your string contains substring that you will want to replace later, like "distance" contains a and if you will want to replace a later with "velocity" you will end up with "disvelocityance".
It can be same problem as if you would like to replace A with B and B with A. For this kind of text manipulation you can use appendReplacement and appendTail from Matcher class. Here is example
String input = "((a+b)/(c+(d*e)))";
Map<String, String> replacementsMap = new HashMap<>();
replacementsMap.put("a", "velocity");
replacementsMap.put("b", "distance");
replacementsMap.put("c", "time");
StringBuffer sb = new StringBuffer();
Pattern p = Pattern.compile("\\b(a|b|c)\\b");
Matcher m = p.matcher(input);
while (m.find())
m.appendReplacement(sb, replacementsMap.get(m.group()));
m.appendTail(sb);
System.out.println(sb);
Output:
((velocity+distance)/(time+(d*e)))
This code will try to find each occurrence of a or b or c which isn't part of some word (it doesn't have any character before or after it - done with help of \b which represents word boundaries). appendReplacement is method which will append to StringBuffer text from last match (or from beginning if it is first match) but will replace found match with new word (I get replacement from Map). appendTail will put to StringBuilder text after last match.
Also to make this code more dynamic, regex should be generated automatically based on keys used in Map. You can use this code to do it
StringBuilder regexBuilder = new StringBuilder("\\b(");
for (String word:replacementsMap.keySet())
regexBuilder.append(Pattern.quote(word)).append('|');
regexBuilder.deleteCharAt(regexBuilder.length()-1);//lets remove last "|"
regexBuilder.append(")\\b");
String regex = regexBuilder.toString();
I'd make a hashMap mapping the variable names to the descriptions, then iterate through all the characters in the string and replace each occurrance of a recognised key with it's mapping.
I would use a StringBuilder to build up the new string.
Using a hashmap and iterating over the string as A Boschman suggested is one good solution.
Another solution would be to do what others have suggested and do a .replaceAll(); however, you would want to use a regular expression to specify that only the words matching the whole variable name and not a substring are replaced. A regex using word boundary '\b' matching will provide this solution.
String variable = "a";
String newVariable = "velocity";
str.replaceAll("\\b" + variable + "\\b", newVariable);
See http://docs.oracle.com/javase/tutorial/essential/regex/bounds.html
For string str, use the replaceAll() function:
str = str.toUpperCase(); //Prevent substitutions of characters in the middle of a word
str = str.replaceAll("A", "velocity");
str = str.replaceAll("B", "distance");
//etc.
Related
I have a string inside tags. Eg: "<abc~a>I am src scr customer<abc~b>"
I want to replace "src" with "abc". I used following regex to replace:-
replacAll("(<abc~a>.?)src(.?<abc~b>)"),"$1"+"abc"+"$2");
But it is replacing only first occurrence of string i.e. output is "<abc~a>I am abc src customer<abc~b>"
I want output as "<abc~a>I am abc abc customer<abc~b>".
I don't want to use matcher pattern. Is there any solution using replaceAll() ? Please help.
We can try using a formal regex pattern matcher here. Match on the pattern <abc~a>(.*?)<abc~a>, and for each match append the tag with src replaced by abc. Here is a sample code:
String input = "Here is a src <abc~a>I am an src customer<abc~b> also another src here.";
Pattern p = Pattern.compile("<abc~a>(.*?)<abc~b>");
Matcher m = p.matcher(input);
StringBuffer buffer = new StringBuffer();
while(m.find()) {
String replace = "<abc~a>" + m.group(1).replaceAll("\\bsrc\\b", "abc") + "<abc~b>";
m.appendReplacement(buffer, replace);
}
m.appendTail(buffer);
System.out.println(buffer.toString());
This prints:
Here is a src <abc~a>I am an abc customer<abc~b> also another src here.
Note that in many other languages we could have used a regex callback function. But core Java does not support this functionality, so we have to iterate over the entire input.
When you’re using Java 9 or newer, the simplest approach to your problem would be
Pattern p = Pattern.compile("(?<=<abc~a>).*?(?=<abc~b>)");
String result = p.matcher(input)
.replaceAll(m -> m.group().replaceAll("\\bsrc\\b", "abc"));
Basically, it does the same as Tim Biegeleisen’s answer under the hood. Minor differences are that it will use StringBuilder instead of StringBuffer, an option only available since Java 9 and it will return the original string instance if no match for the outer pattern (p) has been found (rather than a copy).
I also changed the pattern to use look-behind and look-ahead, which simplifies the replace function and also reduces the amount of character copying.
Note that both replaceAll operations have a similar appendReplacement loop behind the scenes. The method appendReplacement will search for replacement patterns (e.g. $number) in the replacement string, which does not only apply to "abc" but also the entire group between the <abc~a> and <abc~b> tag. If you can’t preclude the presence of conflicting special characters, you have to use Matcher.quoteReplacement to avoid problems.
Besides the unwanted interpretation of replacement patterns, the inner replaceAll will compile the pattern string to a Pattern object on each invocation. Further, the inner operation creates a temporary string which is then used for the outer replacement operation, so this simple solution will copy some of the character contents multiple times.
If performance really matters, it’s worth writing a dedicated operation, even if it’s more verbose.
static final Pattern OUTER_PATTERN = Pattern.compile("<abc~a>(.*?)<abc~b>");
static final Pattern INNER_PATTERN = Pattern.compile("\\bsrc\\b");
String replacement = "abc";
String result;
Matcher m = OUTER_PATTERN.matcher(input);
if(!m.find()) result = input;
else {
StringBuilder sb = new StringBuilder(input.length());
int copyStart = 0, nextSearchStart;
do {
nextSearchStart = m.end();
for(m.region(m.start(1), m.end(1)).usePattern(INNER_PATTERN);
m.find(); copyStart = m.end()) {
sb.append(input, copyStart, m.start()).append(replacement);
}
} while(m.region(nextSearchStart, input.length()).usePattern(OUTER_PATTERN).find());
result = copyStart==0? input: sb.append(input, copyStart, input.length()).toString();
}
This does not compile the patterns multiple times and uses both patterns in a single replacement operation without intermediate steps, performing the minimum character copying necessary. The replacement string is copied literally using StringBuilder.append, so no quoting is necessary. Like the built-in replaceAll it will return the original string when no match of the outer pattern has been found. But it will also return the original string when the outer pattern had matches but there were no inner pattern matches within the affected region(s).
I have a string consisting of 18 digits Eg. 'abcdefghijklmnopqr'. I need to add a blank space after 5th character and then after 9th character and after 15th character making it look like 'abcde fghi jklmno pqr'. Can I achieve this using regular expression?
As regular expressions are not my cup of tea hence need help from regex gurus out here. Any help is appreciated.
Thanks in advance
Regex finds a match in a string and can't preform a replacement. You could however use regex to find a certain matching substring and replace that, but you would still need a separate method for replacement (making it a two step algorithm).
Since you're not looking for a pattern in your string, but rather just the n-th char, regex wouldn't be of much use, it would make it unnecessary complex.
Here are some ideas on how you could implement a solution:
Use an array of characters to avoid creating redundant strings: create a character array and copy characters from the string before
the given position, put the character at the position, copy the rest
of the characters from the String,... continue until you reach the end
of the string. After that construct the final string from that
array.
Use Substring() method: concatenate substring of the string before
the position, new character, substring of the string after the
position and before the next position,... and so on, until reaching the end of the original string.
Use a StringBuilder and its insert() method.
Note that:
First idea listed might not be a suitable solution for very large strings. It needs an auxiliary array, using additional space.
Second idea creates redundant strings. Strings are immutable and final in Java, and are stored in a pool. Creating
temporary strings should be avoided.
Yes you can use regex groups to achieve that. Something like that:
final Pattern pattern = Pattern.compile("([a-z]{5})([a-z]{4})([a-z]{6})([a-z]{3})");
final Matcher matcher = pattern.matcher("abcdefghijklmnopqr");
if (matcher.matches()) {
String first = matcher.group(0);
String second = matcher.group(1);
String third = matcher.group(2);
String fourth = matcher.group(3);
return first + " " + second + " " + third + " " + fourth;
} else {
throw new SomeException();
}
Note that pattern should be a constant, I used a local variable here to make it easier to read.
Compared to substrings, which would also work to achieve the desired result, regex also allow you to validate the format of your input data. In the provided example you check that it's a 18 characters long string composed of only lowercase letters.
If you had a more interesting examples, with for example a mix of letters and digits, you could check that each group contains the correct type of data with the regex.
You can also do a simpler version where you just replace with:
"abcdefghijklmnopqr".replaceAll("([a-z]{5})([a-z]{4})([a-z]{6})([a-z]{3})", "$1 $2 $3 $4")
But you don't have the benefit of checking because if the string doesn't match the format it will just not replaced and this is less efficient than substrings.
Here is an example solution using substrings which would be more efficient if you don't care about checking:
final Set<Integer> breaks = Set.of(5, 9, 15);
final String str = "abcdefghijklmnopqr";
final StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < str.length(); i++) {
if (breaks.contains(i)) {
stringBuilder.append(' ');
}
stringBuilder.append(str.charAt(i));
}
return stringBuilder.toString();
Want to know how to write the correct pattern regex of my url to match this :
key=value .
2 pairs of key=value are Separated by « & » .
Remove key if value is empty or null
Thanks
If you want to remove empty parameters from your query string, you can use this regex \w+=[^&]+ to match only key value pairs whose value part is non-empty. For e.g. if you have following string,
key1=value1&key2=value2&key3=&key4=value4
Then match only URLs using above regex and filter out rest. This Java code should help you,
String s = "key1=value1&key2=value2&key3=&key4=value4";
Pattern p = Pattern.compile("\\w+=[^&]+");
Matcher m = p.matcher(s);
StringBuilder sb = new StringBuilder();
while(m.find()) {
sb.append(m.group()).append("&");
}
System.out.println(sb.substring(0,sb.length()-1));
Prints this which has key3 value removed as it was empty,
key1=value1&key2=value2&key4=value4
Using Java8 streams, you can use this one liner code to achieve,
String s = "key1=value1&key2=value2&key3=&key4=value4";
String cleaned = Arrays.stream(s.split("&")).filter(x -> Pattern.matches("\\w+=[^&]+", x)).collect(Collectors.joining("&"));
System.out.println(cleaned);
Prints,
key1=value1&key2=value2&key4=value4
So I've been trying to make this simple encryption program but I can't seem to figure out some things. The phrase I need to enter is
This is a very big morning.
When I enter it though it returns the string
This is a ag',rery dug>?/ijeb..w ssadorninjeb..w
Instead I return
This is a ajedg>P/..w',rery dg>P/ijedg>P/..w ssadorninjedg>P/..w
I don't understand why and how to fix it? I've been learning java for around a month now so I'm still fresh and if there's a similar question that's already been answered please link me there and I'll delete this post.
Here's the code:
import static java.lang.System.out;
import java.util.Scanner;
class Encryption {
public static void main(String[] args) {
Scanner userInput = new Scanner(System.in);
Crypto user1 = new Crypto();
out.print("Please enter in a sentence: ");
String user = userInput.nextLine();
user1.encrypt(user);
out.print(user1.getEncrypt());
}
}
public Crypto() { }
public String myEn;
public void encrypt(String Sentence) {
myEn = Sentence.replaceAll("v","ag',r")
.replaceAll("m" , "ssad")
.replaceAll("g" , "jeb..w")
.replaceAll("b" , "dg>P/");
}
public String getEncrypt() {
return myEn;
}
}
The reason you're getting the different output is because the chained replaces take the return value of the previous replaces. So in your case, if there was a v, it would be changed with ag',r which has a g in it. That g would then trigger replaceAll("g" , "jeb..w").
To avoid this from happening, you should change the order of the replaces:
Sentence.replaceAll("g" , "jeb..w")
.replaceAll("b" , "dg>P/")
.replaceAll("v","ag',r")
.replaceAll("m" , "ssad");
However, the first two replace statements can't be fixed because one replaces b with a string that has a g in it, and vice-versa, so you might want to change the characters you're replacing.
When you replace the g its replacement contains a b so then when you replace the b's you get all the b's from the g replacement also replaced. and also for the v's
What you could do is
Sentence.replaceAll("g" , "jeb..w")
.replaceFirst("b" , "dg>P/") // as no g's before b's and only want to replace the first
.replaceFirst("v","ag',r")
.replaceFirst("m" , "ssad");
But this only works for this one sentence.
What you could do:
Create a map of all your characters to be replaced and there replacement.
Create a list of the indices of each character that is to be replaced on the original string.
Reverse the order of the list (highest to lowest)
Starting from the first index in the list (the last character to be replaced) replace the character at that index with its replacement from the map
Repeat 4 working backwards down the string.
Firstly, you should use replace(), not replaceAll(). Both replace all matches found, but replaceAll() uses regex to match and replace() uses plain text.
Next, your replacements are getting in the way of each other, so use a StringBuffer and work from the eighth and end backwards replacing one character at a time.
Decryption should likewise work one character at a time starting from the left.
As others already told you problem is that you are replacing characters in few iterations (replaceAll calls) instead of one. If you want to prevent replacing characters that ware used to replace other characters you can use appendReplacement and appendTail from Matcher class.
Here is how you can do it like
// We need to store somewhere info about original and its replacement
// so we will use Map
Map<String, String> replacements = new HashMap<>();
replacements.put("v", "ag',r");
replacements.put("m", "ssad");
replacements.put("g", "jeb..w");
replacements.put("b", "dg>P/");
// we need to create pattern which will find characters we want to replace
Pattern pattern = Pattern.compile("[vmgb]");//this will do
Matcher matcher = pattern.matcher("This is a very big morning.");
StringBuffer sb = new StringBuffer();// this will be used to create
// string with replaced characters
// lets start replacing process
while (matcher.find()) {//first we need to find our characters
// then pick from map its replacement
String replacement = replacements.get(matcher.group());
// and pass it to appendReplacement method
matcher.appendReplacement(sb, replacement);
// we repeat this process until original string has no more
// characters to replace
}
//after all we need to append to StringBuffer part after last replacement
matcher.appendTail(sb);
System.out.println(sb);
Output:
This is a ag',rery dg>P/ijeb..w ssadorninjeb..w.
Voilà.
For example I have such a string, in which I must find and replace multiple substrings, all of which start with #, contains 6 symbols, end with ' and should not contain ) ... what do you think would be the best way of achieving that?
Thanks!
Edit:
just one more thing I forgot, to make the replacement, I need that substring, i.e. it gets replaces by a string generated from the substring being replaced.
yourNewText=yourOldText.replaceAll("#[^)]{6}'", "");
Or programmatically:
Matcher matcher = Pattern.compile("#[^)]{6}'").matcher(yourOldText);
StringBuffer sb = new StringBuffer();
while(matcher.find()){
matcher.appendReplacement(sb,
// implement your custom logic here, matcher.group() is the found String
someReplacement(matcher.group());
}
matcher.appendTail(sb);
String yourNewString = sb. toString();
Assuming you just know the substrings are formatted like you explained above, but not exactly which 6 characters, try the following:
String result = input.replaceAll("#[^\\)]{6}'", "replacement"); //pattern to replace is #+6 characters not being ) + '
You must use replaceAll with the right regular expression:
myString.replaceAll("#[^)]{6}'", "something")
If you need to replace with an extract of the matched string, use a a match group, like this :
myString.replaceAll("#([^)]{6})'", "blah $1 blah")
the $1 in the second String matches the first parenthesed expression in the first String.
this might not be the best way to do it but...
youstring = youstring.replace("#something'", "new stringx");
youstring = youstring.replace("#something2'", "new stringy");
youstring = youstring.replace("#something3'", "new stringz");
//edited after reading comments, thanks