I have a requirement to get the substring of a string based on a condition.
String str = "ABC::abcdefgh||XYZ::xyz";
If input is "ABC", check if it contains in str and if it presents then it should print abcdefgh.
In the same way, if input is "XYZ", then it should print xyz.
How can i achieve this with string manipulation in java?
If I've guessed the format of your String correctly, then you could split it into tokens with something like this:
String[] tokens = str.split("||");
for(String token : tokens)
{
// Cycle through each token.
String key = token.split("::")[0];
String value = token.split("::")[1];
if(key.equals(input))
{
// input being the user's typed in value.
return value;
}
}
But let's have a think for a minute. Why keep this in a String, when a HashMap is a much cleaner solution to your problem? Stick the String into a config file, and on load,
some code can perform a similar task:
Map<String, String> inputMap = new HashMap<String, String>();
String[] tokens = str.split("||");
for(String token : tokens)
{
// Cycle through each token.
String key = token.split("::")[0];
String value = token.split("::")[1];
inputMap.put(key, value);
}
Then when the user types something in, it's as easy as:
return inputMap.get(input);
The idea is that, you should split your string with the delimiters of "::" and "||" , i.e. whichever of them is encountered it will be treated as a delimiter. So, the best way for achieving that is using regular expressions, I think.
String str = "ABC::abcdefgh||XYZ::xyz";
String[] parts = str.split("[::]|[/||]");
Map<String, String> map = new HashMap<String, String>();
for (int i = 0; i < parts.length - 2; i += 4) {
if (!parts[i].equals("")) {
map.put(parts[i], parts[i + 2]);
}
}
Short and concise, your code is ready. The for loop seems weird, if anyone comes up with a better regex for splitting (to get rid of the empty strings), it will become cleaner. I'm not a regex expert, so any suggestions are welcome.
Use the contains method to see if it has the sub string: http://docs.oracle.com/javase/7/docs/api/java/lang/String.html#contains%28java.lang.CharSequence%29
You could do it as follows:
String[] parts = st.split("||");
if (parts[0].startsWith("ABC")) {
String[] values = parts[0].split("::");
System.out.println(values[1]);
} else {
if (parts[1].startsWith("XYZ") {
String[] values = parts[0].split("::");
System.out.println(values[1]);
}
}
The above code will check first if ABC is there. If yes, it will print the result and then stop. If not, it will check the second section of the code to see if it starts with XYZ and then print the result. You can change it to suit your needs.
Related
I am trying to create a function to replace all the bad words in a string with an asterisk in the middle, and here is what I came up with.
public class Censor {
public static String AsteriskCensor(String text){
String[] word_list = text.split("\\s+");
String result = "";
ArrayList<String> BadWords = new ArrayList<String>();
BadWords.add("word1");
BadWords.add("word2");
BadWords.add("word3");
BadWords.add("word4");
BadWords.add("word5");
BadWords.add("word6");
ArrayList<String> WordFix = new ArrayList<String>();
WordFix.add("w*rd1");
WordFix.add("w*rd2");
WordFix.add("w*rd3");
WordFix.add("w*rd4");
WordFix.add("w*rd5");
WordFix.add("w*rd6");
int index = 0;
for (String i : word_list)
{
if (BadWords.contains(i)){
word_list[index] = WordFix.get(BadWords.indexOf(i));
index++;
}
}
for (String i : word_list)
result += i + ' ';
return result;
}
My idea was to break it down into single words, then replace the word if you encounter a bad word, but so far it is not doing anything. Can someone tell me where did I go wrong? I am quite new to the language
If you move the index++ to out of the if statement, then your code works fine.
Online demo
However, it won't work properly if there are any punctuation marks immediately following a word to be censored. For example, the sentence "We have word1 to word6, and they are censored", then only "word1" will be censored, due to the comma immediately following the word.
I personally would approach this differently. Instead of maintaining two lists, you could also create a Map which maps the bad words to their censored counterparts:
static String censor(String text) {
Map<String, String> filters = Map.of(
"hello", "h*llo",
"world", "w*rld",
"apple", "*****"
);
for (var filter : filters.entrySet()) {
text = text.replace(filter.getKey(), filter.getValue());
}
return text;
}
Of course, this is code is still a little naive, because it will also filter words like 'applet', because the word 'applet' contains 'apple'. That's probably not what you want.
Instead, we need to tweak the code a little, so the found words must be whole words, that is, not part of another word. You can fix this by replacing the body of the for loop by this:
String pattern = "\\b" + Pattern.quote(filter.getKey()) + "\\b";
text = text.replaceAll(pattern, filter.getValue());
It replaces text using a regular expression. The \b is a word-boundary character, which makes sure it only matches the start or end of a word. This way, words like 'dapple' and 'applet' are no longer matched.
Online demo
I have a question about replacing words. I have some strings, each of which looks like this:
String string = "today is a (happy) day, I would like to (explore) more about Java."
I need to replace the words that have parentheses. I want to replace "(happy)" with "good", and "(explore)" with "learn".
I have some ideas, but I don't know how.
for (int i = 0; i <= string.length(), i++) {
for (int j = 0; j <= string.length(), j++
if ((string.charAt(i)== '(') && (string.charAt(j) == ')')) {
String w1 = line.substring(i+1,j);
string.replace(w1, w2)
}
}
}
My problem is that I can only replace one word with one new word...
I am thinking of using a scanner to prompt me to give a new word and then replace it, how can I do this?
The appendReplacement and appendTail methods of Matcher are designed for this purpose. You can use a regex to scan for your pattern--a pair of parentheses with a word in the middle--then do whatever you need to do to determine the string to replace it with. See the javadoc.
An example, based on the example in the javadoc. I'm assuming you have two methods, replacement(word) that tells what you want to replace the word with (so that replacement("happy") will equal "good" in your example), and hasReplacement(word) that tells whether the word has a replacement or not.
Pattern p = Pattern.compile("\\((.*?)\\)");
Matcher m = p.matcher(source);
StringBuffer sb = new StringBuffer();
while (m.find()) {
String word = m.group(1);
String newWord = hasReplacement(word) ? replacement(word) : m.group(0);
m.appendReplacement(sb, newWord); // appends the replacement, plus any not-yet-used text that comes before the match
}
m.appendTail(sb); // appends any text left over after the last match
String result = sb.toString();
Use below code for replacing the string.
String string = "today is a (happy) day, I would like to (explore) more about Java.";
string = string.replaceAll("\\(happy\\)", "good");
string = string.replaceAll("\\(explore\\)", "learn");
System.out.println(string);`
What you can do is run a loop from 0 to length-1 and if loop encounters a ( then assign its index to a temp1 variable. Now go on further as long as you encounter ).Assign its index to temp2 .Now you can replace that substring using string.replace(string.substring(temp1+1,temp2),"Your desired string")).
No need to use the nested loops. Better use one loop and store the index when you find opening parenthesis and also for close parenthesis and replace it with the word. Continue the same loop and store next index. As you are replacing the words in same string it changes the length of string you need to maintain copy of string and perform loop and replace on different,
Do not use nested for loop. Search for occurrences of ( and ). Get the substring between these two characters and then replace it with the user entered value. Do it till there are not more ( and ) combinations left.
import java.util.Scanner;
public class ReplaceWords {
public static String replaceWords(String s){
while(s.contains(""+"(") && s.contains(""+")")){
Scanner keyboard = new Scanner(System.in);
String toBeReplaced = s.substring(s.indexOf("("), s.indexOf(")")+1);
System.out.println("Enter the word with which you want to replace "+toBeReplaced+" : ");
String replaceWith = keyboard.nextLine();
s = s.replace(toBeReplaced, replaceWith);
}
return s;
}
public static void main(String[] args) {
String myString ="today is a (happy) day, I would like to (explore) more about Java.";
myString = replaceWords(myString);
System.out.println(myString);
}
}
This snippet works for me, just load the HashMap up with replacements and iterate through:
import java.util.*;
public class Test
{
public static void main(String[] args) {
String string = "today is a (happy) day, I would like to (explore) more about Java.";
HashMap<String, String> hm = new HashMap<String, String>();
hm.put("\\(happy\\)", "good");
hm.put("\\(explore\\)", "learn");
for (Map.Entry<String, String> entry : hm.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
string = string.replaceAll(key, value);
}
System.out.println(string);
}
}
Remember, replaceAll takes a regex, so you want it to display "\(word\)", which means the slashes themselves must be escaped.
I'm a computer science student learning Java, and as an exercise, we're doing a permutation algorhythm.
Now, i'm stuck at a point where i need to search for a natural number within a String full of numbers, splitted by a comma:
String myString = "0,1,2,10,14,";
The problem is i'm using...
myString.contains(String.valueOf(anInteger);
...to check for the presence of a specific number. This works for numbers from 0 to 9, but when looking for a more-than-1-digit number, the program does not recognize it as a natural number.
In other words, and as an example: "14" is not the integer 14, its just a string with an "1", and a "4"; so, if i run...
String myString = "0,1,2,10,14,";
if (myString.contains(myString.valueOf(4))) { doSomething(); }
...the "if" statement will be true, since the integer "4" is present in the string, as part of the natural number "14".
At this point, i've been searching through StackOverflow and other pages for a solution, and learnt i should use Pattern and Matcher.
My question is: what's the best way to do use them?
Relevant part of my code:
for (int i = 0; i<r; i++)
{
if (!act.contains(String.valueOf(i)))
{
...
}
...
}
I use this method several times in my code, so an exact substitution would be nice.
Thank you all in advance!
You only need a method call to matches():
if (myString.matches(".*\\b" + anInteger + "\\b.*"))
// string contains the number
This works using by creating a regex that has a word boundary (\b) at either end of the target number. The leading and trailing .* are required because matches() must match the whole string to return true.
Look into how to split a String into an array of String. So:
String[] splitStrings = myString.split(",")
ArrayList<Integer> parsedInts = new ArrayList<Integer>();
for (String str : splitStrings) {
parsedInts.add(Integer.parseInt(str));
}
then in your for loop:
if (parsedInts.contains(i)) {
// body
}
Something like this:
String myString = "0,1,2,10,14,";
String[] split = myString.split(",");
for (String string : split) {
int num = Integer.parseInt(string);
if (num == 4) {
System.out.println(num);
// ...
}
}
String myString = "0,1,2,10,14,2323232";
String[] allList = myString.split(",");
for (String string : allList) {
if(string.matches("[0-9]*"))
{
System.out.println("Its number with value "+string);
}
}
I think you need to pick all the numbers in the given string and find the permutation.
I think you need to Tokenize the given string with the Comma Separator.
When I do such program, I divide my logic to parse the String and write the logic in another method. Below is the snippet
String myString = "0,1,2,10,14,";
StringTokenizer st2 = new StringTokenizer(myString , ",");
while (st2.hasMoreElements()) {
doSomething(st2.nextElement());
}
passing 0 as a limit argument prevents trailing empty strings, but how does one prevent leading empty strings?
for instance
String[] test = "/Test/Stuff".split("/");
results in an array with "", "Test", "Stuff".
Yeah, I know I could roll my own Tokenizer... but the API docs for StringTokenizer say
"StringTokenizer is a legacy class that is retained for compatibility
reasons although its use is discouraged in new code. It is recommended
that anyone seeking this functionality use the split"
Your best bet is probably just to strip out any leading delimiter:
String input = "/Test/Stuff";
String[] test = input.replaceFirst("^/", "").split("/");
You can make it more generic by putting it in a method:
public String[] mySplit(final String input, final String delim)
{
return input.replaceFirst("^" + delim, "").split(delim);
}
String[] test = mySplit("/Test/Stuff", "/");
Apache Commons has a utility method for exactly this: org.apache.commons.lang.StringUtils.split
StringUtils.split()
Actually in our company we now prefer using this method for splitting in all our projects.
I don't think there is a way you could do this with the built-in split method. So you have two options:
1) Make your own split
2) Iterate through the array after calling split and remove empty elements
If you make your own split you can just combine these two options
public List<String> split(String inString)
{
List<String> outList = new ArrayList<>();
String[] test = inString.split("/");
for(String s : test)
{
if(s != null && s.length() > 0)
outList.add(s);
}
return outList;
}
or you could just check for the delimiter being in the first position before you call split and ignore the first character if it does:
String delimiter = "/";
String delimitedString = "/Test/Stuff";
String[] test;
if(delimitedString.startsWith(delimiter)){
//start at the 1st character not the 0th
test = delimitedString.substring(1).split(delimiter);
}
else
test = delimitedString.split(delimiter);
I think you shall have to manually remove the first empty string. A simple way to do that is this -
String string, subString;
int index;
String[] test;
string = "/Test/Stuff";
index = string.indexOf("/");
subString = string.substring(index+1);
test = subString.split("/");
This will exclude the leading empty string.
I think there is no built-in function to remove blank string in Java. You can eliminate blank deleting string but it may lead to error. For safe you can do this by writing small piece of code as follow:
List<String> list = new ArrayList<String>();
for(String str : test)
{
if(str != null && str.length() > 0)
{
list.add(str);
}
}
test = stringList.toArray(new String[list.size()]);
When using JDK8 and streams, just add a skip(1) after the split. Following sniped decodes a (very wired) hex encoded string.
Arrays.asList("\\x42\\x41\\x53\\x45\\x36\\x34".split("\\\\x"))
.stream()
.skip(1) // <- ignore the first empty element
.map(c->""+(char)Integer.parseInt(c, 16))
.collect(Collectors.joining())
You can use StringTokenizer for this purpose...
String test1 = "/Test/Stuff";
StringTokenizer st = new StringTokenizer(test1,"/");
while(st.hasMoreTokens())
System.out.println(st.nextToken());
This is how I've gotten around this problem. I take the string, call .toCharArray() on it to split it into an array of chars, and then loop through that array and add it to my String list (wrapping each char with String.valueOf). I imagine there's some performance tradeoff but it seems like a readable solution. Hope this helps!
char[] stringChars = string.toCharArray();
List<String> stringList = new ArrayList<>();
for (char stringChar : stringChars) {
stringList.add(String.valueOf(stringChar));
}
You can only add statement like if(StringUtils.isEmpty(string)) continue; before print the string. My JDK version 1.8, no Blank will be printed.
5
this
program
gives
me
problems
If I am being passed a string that contains comma delimited key-value pairs like this
seller=1000,country="canada",address="123 1st st", etc.
There seems like there must be a better way than parsing then iterating through.
What is the best way to retreive a value from this string based on the key name in Java?
Since release 10 Google Guava provides a class MapSplitter which does exactly that kind of things:
Map<String, String> params = Splitter
.on(",")
.withKeyValueSeparator("=")
.split("k1=v1,k2=v2");
You can create your own CSV parser, it's not very complicated but there are a few corner cases to be carfull with assuming of course you are using standard CSV format.
But why reinventing the wheel...
You can try looking up a CSV parser like
OpenCSV
SuperCSV
Apache Commons
There are others, look around I'm sure you will find one that suits your needs.
Usually you will want to parse the string into a map because you will be pulling various values perhaps multiple times, so it often makes sense to pay the parsing cost up-front.
If not, then here is how I would solve the problem (assuming you want to differentiate between int values and String values).:
public Object pullValue(String pairs, String key) {
boolean returnString = false;
int keyStart = pairs.indexOf(key + "=");
if (keyStart < 0) {
logger.error("Key " + key + " not found in key-value pairs string");
return null;
}
int valueStart = keyStart + key.length() + 1;
if (pairs.charAt(valueStart) == '"') {
returnString = true;
valueStart++; // Skip past the quote mark
}
int valueEnd;
if (returnString) {
valueEnd = pairs.indexOf('"', valueStart);
if (valueEnd < 0) {
logger.error("Unmatched double quote mark extracting value for key " + key)
}
return pairs.substring(valueStart, valueEnd);
} else {
valueEnd = pairs.indexOf(',', valueStart);
if (valueEnd < 0) { // If this is the last key value pair in string
valueEnd = pairs.length();
}
return Integer.decode(pairs.substring(valueStart, valueEnd));
}
}
Note that this solution assumes no spaces between the key, the equals sign, and the value. If these are possible you will have to create some code to travel the string between them.
Another solution is to use a regular expression parser. You could do something like (this is untested):
Pattern lookingForString = Pattern.compile(key + "[ \t]*=[ \t]*[\"]([^\"]+)[\"]");
Pattern lookingForInt = Pattern.compile(key + "[ \t]*=[ \t]*([^,]+)");
Matcher stringFinder = lookingForString.matcher(pairs);
Matcher intFinder = lookingForInt.matcher(pairs);
if (stringFinder.find()) {
return stringFinder.group(1);
} else if (intFinder.find()) {
return Integer.decode(intFinder.group(1));
} else {
logger.error("Could not extract value for key " + key);
return null;
}
HTH
To separate the string by commas, the other posters are correct. It is best to use a CSV parser (your own or OTS). Considering things like commas inside quotes etc can lead to a lot of un-considered problems.
Once you have each separate token in the form:
key = "value"
I think it is easy enough to look for the first index of '='. Then the part before that will be the key, and the part after that will be the value. Then you can store them in a Map<String, String>.
This is assuming that your keys will be simple enough, and not contain = in them etc. Sometimes it's enough to take the simple route when you can restrict the problem scope.
If you just want one value out of such a string, you can use String's indexOf() and substring() methods:
String getValue(String str, String key)
{
int keyIndex = str.indexOf(key + "=");
if(keyIndex == -1) return null;
int startIndex = str.indexOf("\"", keyIndex);
int endIndex = str.indexOf("\"", startIndex);
String value = str.substring(startIndex + 1, endIndex);
return value;
}
First thing you should use a CSV parsing library to parse the comma separated values. Correctly parsing CSV data isn't as trivial as it first seems. There are lots of good arguments to not reinvent that wheel.
This will also future proof your code and be code you don't have to test or maintain.
I know the temptation to do something like data.split(','); is strong, but it is fragile and brittle solution. For just one example, what if any of the values contain the ','.
Second thing you should do is then parse the pairs. Again the temptation to use String.split("="); will be strong, but it can be brittle and fragile if the right hand side of the = has an = in it.
I am not a blind proponent of regular expressions, but used with restraint they can be just the right tool for the job. Here is the regular expression to parse the name value pairs.
The regular expression ^(.*)\s?=\s?("?([^"]*)"?|"(.*)")$, click the regular expression to test it online interactively. This works even for multiple double quotes in the right hand side of the name value pair.
This will match only what is on the left side of the first = and everything else on the right hand side, and strip the optional " off the string values, while still matching the non-quoted number values.
Given a List<String> list of the encoded name value pairs.
final Pattern p = Pattern.compile("^(.*)\s?=\s?("?([^"]*)"?|"(.*)")$");
final Map<String, String> map = new HashMap<String, String>(list.size());
for (final String nvp : list)
{
final Matcher m = p.matcher(nvp);
m.matches();
final String name = m.group(1);
final String value = m.group(2);
System.out.format("name = %s | value = %s\n", name, value);
}
Use String.split(yourdata, ',') and you will get a String[]. Then, perform String.split(String[i],"="), on each entry to separate property and values.
Ideally, you should move this data in a Properties object instance. You can then save/load it from XML easily. It has useful methods.
REM: I am assuming that you are savvy enough to understand that this solution won't work if values contain the separator (i.e., the comma) in them...