I have a string which contains an underscore as shown below:
123445_Lisick
I want to remove all the characters from the String after the underscore. I have tried the code below, it's working, but is there any other way to do this, as I need to put this logic inside a for loop to extract elements from an ArrayList.
public class Test {
public static void main(String args[]) throws Exception {
String str = "123445_Lisick";
int a = str.indexOf("_");
String modfiedstr = str.substring(0, a);
System.out.println(modfiedstr);
}
}
Another way is to use the split method.
String str = "123445_Lisick";
String[] parts = string.split("_");
String modfiedstr = parts[0];
I don't think that really buys you anything though. There's really nothing wrong with the method you're using.
Your method is fine. Though not explicitly stated in the API documentation, I feel it's safe to assume that indexOf(char) will run in O(n) time. Since your string is unordered and you don't know the location of the underscore apriori, you cannot avoid this linear search time. Once you have completed the search, extraction of the substring will be needed for future processing. It's generally safe to assume the for simple operations like this in a language which is reasonably well refined the library functions will have been optimized.
Note however, that you are making an implicit assumption that
an underscore will exist within the String
if there are more than one underscore in the string, all but the first should be included in the output
If either of these assumptions will not always hold, you will need to make adjustments to handle those situations. In either case, you should at least defensively check for a -1 returned from indexAt(char) indicating that '_' is not in the string. Assuming in this situation the entire String is desired, you could use something like this:
public static String stringAfter(String source, char delim) {
if(source == null) return null;
int index = source.indexOf(delim);
return (index >= 0)?source.substring(index):source;
}
You could also use something like that:
public class Main {
public static void main(String[] args) {
String str = "123445_Lisick";
Pattern pattern = Pattern.compile("^([^_]*).*");
Matcher matcher = pattern.matcher(str);
String modfiedstr = null;
if (matcher.find()) {
modfiedstr = matcher.group(1);
}
System.out.println(modfiedstr);
}
}
The regex groups a pattern from the start of the input string until a character that is not _ is found.
However as #Bill the lizard wrote, i don't think that there is anything wrong with the method you do it now. I would do it the same way you did it.
Related
I have a utility class to resolve a string input with certain patterns as shown in the example below. All variables are surrounded by { and }. If my string is something like Language is {lang} and version 2 is {version}. Home located at {java.home} the output is Language is java and version 2 is 1.8. Home located at C:/java and if my string is like Language is {lang} and version 2 is {version}. Home located at {{lang}.home} the output is Language is java and version 2 is 1.8. Home located at {java.home}. All I am trying to find is a way to resolve nested properties recursively but ran into several issues. Can any logic be inserted into the code so that resolving of inner properties happen dynamically?
import java.util.*;
import java.util.regex.*;
public class MyClass {
public static void main(String args[]) {
System.setProperty("lang" , "java");
System.setProperty("version" , "1.8");
System.setProperty("java.home" , "C:/java");
System.out.println(resolve("Language is {lang} and version 2 is {version}. Home located at {java.home}"));
System.out.println(resolve("Language is {lang} and version 2 is {version}. Home located at {{lang}.home}"));
}
public static String resolve(String input) {
List<String> tokens = matchers("[{]\\S+[}]", input);
String value;
for(String token : tokens) {
value = getProperty(token);
if (null != value) {
input = input.replace(token, value);
}
value = "";
}
return input;
}
private static String getProperty(String key) {
key = key.substring(1, key.length()-1);
return System.getProperty(key);
}
public static List<String> matchers(String regex, String text) {
List<String> matches = new ArrayList<String>();
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(text);
while (matcher.find()) {
matches.add(matcher.group());
}
return matches;
}
public static boolean contains(String regex, String text) {
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(text);
return matcher.find();
}
}
You just have to ask for the pattern to get only the value without an inner { or } with [^{}]. No "curly bracket" means no inner values. So you can safely do the replace.
First, we create a Pattern, we need to escape those {}... and we add a capture group for later.
Pattern p = Pattern.compile("\\{([^{}]+)\\}");
Then we check with the current value:
Matcher m = p.matcher(s);
Now, we just have to check if there is a match and loop on it.
while( m.find() ){
...
}
In there, we will need the value captured, so we get the first group and get its value (let assume it will always be present) :
String key = m.group(1);
String value = properties.get(key); //add some fail safe.
Using the Matcher.replaceFirst, we will safely replace only the current match (the one we get the value from). If you use replaceAll, it will replace every pattern with the same value.
s = m.replaceFirst(properties.get(key));
Now, since we have updated the String, we need to call check the regex again :
m = p.matcher(s);
Here is a full example:
Map<String, String> properties = new HashMap<>();
properties.put("lang", "java");
properties.put("java.version", "1.8");
String s = "This is {{lang}.version}.";
Pattern p = Pattern.compile("\\{([^{}]+)\\}");
Matcher m = p.matcher(s);
while(m.find()){
String key = m.group(1);
s = m.replaceFirst(properties.get(key));
System.out.println(s);
m = p.matcher(s); //Reset the matcher
}
This is {java.version}.
This is 1.8.
This has one problem, it will required to a lot of Matcher initialisation, so it might not be optimal. Of course, it is most likely not optimized (not the point here)
FYI : Using the Matcher.replaceFirst instead of the String.replaceFirst prevent a new Pattern compilation to be done. Here is the String.replaceFirst code :
public String replaceFirst(String regex, String replacement) {
return Pattern.compile(regex).matcher(this).replaceFirst(replacement);
}
We already have a Matcher to do that, so use it.
There are lots of ways you could achieve this.
You need some way to communicate to the caller either whether a replacement is necessary, or whether one was made.
A simple option:
public boolean hasPlaceholder(String s) {
// return true if s contains a {} placeholder, else false
}
Using this you can repeatedly replace until done:
while(hasPlaceholder(s)) {
s = replacePlaceholders(s);
}
This does scan through the string more times than is strictly necessary, but you shouldn't optimise prematurely.
A more sophisticated option is for the replacePlaceholders() method to report back whether it succeeded. For that you'll need a response class that wraps the result String and the wasReplaced() boolean:
ReplacementResult replacePlaceholders(String s) {
// process string into newString, counting placeholders replaced
return new ReplacementResult(count > 0, newString);
}
(Implementation of ReplacementResult left as an exercise)
Using this you can do:
ReplacementResult result = replacePlaceholders(s);
while(result.wasReplaced()) {
result = replacePlaceholders(result.string());
}
So, each time you call replacePlaceholders() it will either make at least one replacement, or it will report false having verified that there are no more replacements to make.
You mention recursion in the question. This can of course be done, and it would mean avoiding scanning through the whole string each time -- as you can look at just the replacement fragment. This is untested Java-like pseudocode:
String replaceRecursively(String s) {
StringBuilder result = new StringBuilder();
while(Token token = takeTokenFrom(s)) {
if(token.isPlaceholder()) {
String rawReplacement = lookupReplacement(token);
String processedReplacement = replaceRecursively(rawReplacement);
result.append(processedReplacement);
} else {
result.append(token.text());
}
}
return result.toString();
}
For all of these solutions, you should beware of infinite loops or stack-blowing recursion. What if you replace "{foo}" with "{foo}"? (or worse, what if you replace "{foo}" with "{foo}{foo}"!?).
Of course the simplest way is to be in control of the configuration, and simply not trigger that problem. Detecting the problem programatically is entirely possible, but complex enough that it would warrant another SO question if you want it.
Is there a way to find of most precise regex for a string?
For e.g.
Lets say, I have 2 regex:
1) .*bourne
2) .*ne
If I try to match Melbourne with the above regex, it will match with both regex.
But more precise match will be the first regex. Similarly, there can be very complex regex.
Is there a way to find the most precise match?
Is there a way to find the most precise match?
The most "precise" match is the the one where the regex needs to process less data until it finds a match, in this case, .*bourne.
Wouldn't sorting the patterns in descending order of length solve the problem ?
For example, if Java is the language being used something like the following should be fine right (just sort the pattern in descending order of length and then return for first match)?
public class TestPattern {
public static void main(String args[]){
String text ="Melbourne";
System.out.println("Mtaching regex --> "+getMatchingRegex(text));
}
public static String getMatchingRegex(String text) {
ArrayList<String> patterns = new ArrayList<String>();
patterns.add(".*ne") ;
patterns.add(".*urne") ;
patterns.add(".*bourne") ;
patterns.add(".*rne") ;
Collections.sort(patterns, new StringComparator());
for(String pattern:patterns) {
if(Pattern.matches(pattern, text))
return pattern;
}
return "No Regex matched";
}
public static class StringComparator implements Comparator<String>
{
#Override
public int compare(String s1, String s2)
{
return s2.length()-s1.length();
}
}
}
I'm very new at Java and I have a question about a summer assignment. These are the instructions:
Write a class called SpecialToken that has a static method called thirdToken. This
method should return as a String, the third token of a String that you pass as a parameter.
You may assume that spaces will serve as delimiters.
This is what I have so far but honestly I am stumped at what the parameter should be and how to return the third token! I was thinking I could do something like nextToken() until the third.
public class SpecialToken {
public static String thirdToken() {
}
}
Try something like
public class SpecialToken {
public static String thirdToken(String str) {
String[] splited = str.split(" ");
return splited[2];
}
}
Also see this tutorial or try searching google for "java split string into array by space"
Also note, as Betlista said this does not have any error checking, so if the passed string only has two tokens delimited by one space, you will get an Array out of bounds exception.
Or an other way would be to "Use StringTokenizer to tokenize the string. Import java.util.StringTokenizer. Then create a new instance of a StringTokenizer with the string to tokenize and the delimiter as parameters. If you do not enter the delimiter as a parameter, the delimiter will automatically default to white space. After you have the StringTokenizer, you can use the nextToken() method to get each token. " via Wikihow
With this method, your code should look something like this:
public class SpecialToken {
public static String thirdToken(String str) {
StringTokenizer tok = new StringTokenizer(str); // If you do not enter the delimiter as a parameter, the delimiter will automatically default to white space
int n = tok.countTokens();
if (n < 3) {return "";}
tok.nextToken();
tok.nextToken();
return tok.nextToken();
}
}
However keep in mind Wikihow's warning "now, the use of StringTokenizer is discouraged and the use of the split() method in the String class or the use of the java.util.regex package is encouraged."
I wrote a recursive method that gets all possible character combinations from the characters in a string. I also have a method to access it and return a list of the combos:
public static void uns(String word, StringBuilder s, List combos)
{
for(char c: word.toCharArray())
{
s.append(c);
if(word.length() != 1)
{
uns(removeChar(word, c),s,combos);
}
else
{
combos.add(s.toString());
}
s.deleteCharAt(s.toString().length()-1);
}
}
public static List getCombinations(String word)
{
List<String> combinations = new ArrayList<String>();
uns(word,new StringBuilder(),combinations);
return combinations;
}
public static String removeChar(String s, char c)
{
int index = s.indexOf(c);
return s.substring(0,index)+s.substring(index+1);
}
When testing it in Java, it ran with no flaws. For some reason, when I use it in Android, the list is populated with the correct number of elements, but every element is the same. For instance, for the word "here", it returns a list filled with "eerh".
This is a very weird glitch (definitely reproducible) and you may want to file a bug report on this.
However, here is a temporary workaround; instead of using .toString(), which appears to somehow reuse the reference (even if I do .substring(0) with it), so all of them get updated; if you print out the list after each iteration, you'll see what I mean.
Here is my hacky/inefficient solution. Change:
combos.add(s.toString());
... to:
combos.add(s + "");
This effectively clones the string properly into the array, so that they are not manipulated:
02-17 19:33:48.605: I/System.out(6502): [Combos]: [here, heer, hree, hree, here, heer, ehre, eher, erhe, ereh, eehr, eerh, rhee, rhee, rehe, reeh, rehe, reeh, ehre, eher, erhe, ereh, eehr, eerh]
I'm not positive but i think the valueOf() method from the string class will work also. maybe try using a List instead of the StringBuilder, add characters to the list and try String.valueOf(s.get(i)); and that should convert the character to a string. i don't see why out wouldn't work in Android but you may need to modify your loop a little. hope that helps.
I have a question involving the Replace Method. I saw a question similar to this on here, but I tried to do the replaceFirst but it didn't work for me. Is there, any way I can use the replace method to change a string that results in: Helle, Werld; to get it to result to BE "Hello, World" using the replace method. Is there a way using the replaceFirst method for me to search for the sequence of "le" and replace it with "lo" and also change "We" to "Wo"?. Please see my code below:
public class Printer
{
/**Description: Replacement class
*
*
*/
public static void main(String[] args)
{
String test1Expected = "Hello, World!";
String newString1;
String test1 = "Holle, Werld!";
newString1 = test1.replace('o', 'e');
//Could I do: newString1.replaceFirst("le","lo);
System.out.println("newString1 = " + newString1);
//Output comes out to "Helle, Werld!"
}
}
You can do two regular expressions separatelt one after the other. Please try the following
newString1 = newString1.replaceAll("le", "lo").replaceAll("We", "Wo");