Java - Extract string from pattern - java

Given some strings that look like this:
(((((((((((((4)+13)*5)/1)+7)+12)*3)-6)-11)+9)*2)/8)-10)
(((((((((((((4)+13)*6)/1)+5)+12)*2)-7)-11)+8)*3)/9)-10)
(((((((((((((4)+13)*6)/1)+7)+12)*2)-8)-11)+5)*3)/9)-10)
(btw, they are solutions for a puzzle which I write a program for :) )
They all share this pattern
"(((((((((((((.)+13)*.)/.)+.)+12)*.)-.)-11)+.)*.)/.)-10)"
For 1 solution : How can I get the values with this given pattern?
So for the first solution I will get an collection,list,array (doesn't matter) like this:
[4,5,1,7,3,6,9,2,8]

You've done most of the work actually by providing the pattern. All you need to do is use capturing groups where the . are (and escape the rest).
I put your inputs in a String array and got the results into a List of integers (as you said, you can change it to something else). As for the pattern, you want to capture the dots; this is done by surrounding them with ( and ). The problem in your case is that the whole string is full of them, so we need to quote / escape them out (meaning, tell the regex compiler that we mean the literal / character ( and )). This can be done by putting the part we want to escape between \Q and \E.
The code below shows a coherent (though maybe not effective) way to do this. Just be careful with using the right amount of \ in the right places:
public class Example {
public static void main(String[] args) {
String[] inputs = new String[3];
inputs[0] = "(((((((((((((4)+13)*5)/1)+7)+12)*3)-6)-11)+9)*2)/8)-10)";
inputs[1] = "(((((((((((((4)+13)*6)/1)+5)+12)*2)-7)-11)+8)*3)/9)-10)";
inputs[2] = "(((((((((((((4)+13)*6)/1)+7)+12)*2)-8)-11)+5)*3)/9)-10)";
List<Integer> results;
String pattern = "(((((((((((((.)+13)*.)/.)+.)+12)*.)-.)-11)+.)*.)/.)-10)"; // Copy-paste from your question.
pattern = pattern.replaceAll("\\.", "\\\\E(.)\\\\Q");
pattern = "\\Q" + pattern;
Pattern p = Pattern.compile(pattern);
Matcher m;
for (String input : inputs) {
m = p.matcher(input);
results = new ArrayList<>();
if (m.matches()) {
for (int i = 1; i < m.groupCount() + 1; i++) {
results.add(Integer.parseInt(m.group(i)));
}
}
System.out.println(results);
}
}
}
Output:
[4, 5, 1, 7, 3, 6, 9, 2, 8]
[4, 6, 1, 5, 2, 7, 8, 3, 9]
[4, 6, 1, 7, 2, 8, 5, 3, 9]
Notes:
You are using a single ., which means
Any character (may or may not match line terminators)
So if you have a number there which is not a single digit or a single character which is not a number (digit), something will go wrong either in the matches or parseInt. Consider \\d to signify a single digit or \\d+ for a number instead.
See Pattern for more info on regex in Java.

Related

Indices of all the overlapping patterns using Pattern & Matcher [duplicate]

This question already has an answer here:
Returning overlapping regular expressions
(1 answer)
Closed 4 years ago.
How do I get all the indices(including overlapping) of the string where a pattern is matching.
I have this poc code.
public static void main(){
String input = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
Pattern pattern = Pattern.compile("aaa");
Matcher matcher = pattern.matcher(input);
List<Integer> all = new ArrayList<>();
while (matcher.find()) {
all.add(matcher.start());
}
System.out.println(all);
}
Output:
[0, 3, 6, 9, 12, 15, 18, 21, 24, 27]
It does not consider overlapping patterns.
All the matching indices should be:
[0, 1, 2, 3, 4, .....27]
I know it is easily doable by KMP, but
Can we do it using Pattern and Matcher?
You can change your regex so that the entire expression is within a lookahead, i.e. change "aaa" to "(?=aaa)". This way, the matcher will find overlapping matches, although the matches are not really overlapping, as the actual match will be empty. You can still use groups in the lookahead, though. As a more complex example (Online Demo):
String input = "abab1ab2ab3bcaab4ab5ab6";
Pattern pattern = Pattern.compile("(?=((?:ab.){2}))");
Matcher matcher = pattern.matcher(input);
while (matcher.find()) {
System.out.println(matcher.start() + " " + matcher.group(1));
}
Starting indices and groups are:
2 ab1ab2
5 ab2ab3
14 ab4ab5
17 ab5ab6

How to split a string into only positive and negative integers?

I'm writing a program to do different calculations with vector functions, but the program I have as of now delimits the negative digits. I've tried using different delimiters but I can't seem to get the right one.
Does anyone know how to keep the positive and negative digits when splitting a string? Also, is there a way to keep any decimal values? .45 would return 45 and .29 would return 29
This is the code:
ArrayList<Integer> list = new ArrayList<Integer>();
String twoVectors = "a=<-1,2,-3> b=<4,-5,6>"; // vector and a and b
String[] tokens = twoVectors.split("\\D");
for (String s : tokens)
if (!s.equals(""))
list.add(Integer.parseInt(s));
System.out.println(Arrays.toString(list.toArray()));
When I run the program I get [1, 2, 3, 4, 5, 6] instead of [-1, 2, -3, 4, -5, 6]. All the functions I have worked perfectly fine but dont work when using negative values.
Any help would be appreciated.
You can use
String[] tokens = twoVectors.split("[^\\d-]+");
[^\\d-]+ : match anything except digits and -
[] : match everything mentioned inside []
^ : negation mean do not match (\\d-)
\\d- : digits 0-9 and - character
Regex Demo
String twoVectors = "a=<-1,2,-3> b=<4,-5,6>";
ArrayList<Integer> list = new ArrayList<Integer>();
String[] tokens = twoVectors.split("[^\\d-]");
for (String s : tokens)
if (!s.equals(""))
list.add(Integer.parseInt(s));
System.out.println(Arrays.toString(list.toArray()));
Output :
[-1, 2, -3, 4, -5, 6]
Or
you can use Pattern along with matcher to find all the desired values i.e singed or unsigned numbers with -?\\d+ regex
Regex Demo -?\d+
Update : For Double values , you can use [^\\d-.]+ and make sure to use Double instead of Integer along with Double.parseDouble
And with Pattern and Matcher use -?\\d*\\.?\\d+
Use [^\\d-] inside your split method i.e. twoVectors.split("[^\\d-]")
Why [^\\d-]:
^ : Finds regex that must match at the beginning of the line.
\d : Any digit from [0-9]
- : will match '-' if it exists
The regex that you currently have splits the string on anything but digits. So anything that is not a digit is considered a splitter. If you added - sign to this pattern, anything that is not a digit or a - sign will be included. This will work for some cases, but will fail if you have - or . without a number afterwards.
What you need to do is to specify the number format in a regex (like -?\d*.?\d+), and then find all matches of this pattern. You will also need to change the numbers to Double so that you can parse decimal numbers.
String twoVectors = "a=<-1,.2,-3> b=<4,-5,6>";
ArrayList<Double> numbers = new ArrayList<Double>();
Matcher matcher = Pattern.compile("-?\\d*\\.?\\d+").matcher(twoVectors);
while (matcher.find()) {
numbers.add(Double.parseDouble(matcher.group()));
}
System.out.println(Arrays.toString(numbers.toArray()));
Output
[-1.0, 0.2, -3.0, 4.0, -5.0, 6.0]
A 1-line solution:
List<Integer> numbers = Arrays
.stream(twoVectors.replaceAll("^[^\\d-]+", "").split("[^\\d-]+"))
.map(Integer::new)
.collect(Collectors.toList());
The initial replace is to remove the leading non-target chars (otherwise the split would return a blank in the first element).

Java Regex : 4 Letters followed by 2 Integers

Regex beginner here.
Already visited the followings, none answers my question :
1, 2, 3, 4, 5, 6, etc.
I have a simple regex to check if a string contains 4 chars followed by 2 digits.
[A-Za-z]{4}[0-9]{2}
But, when using it, it doesn't matches. Here is the method I use and an example of input and output :
Input in a JPasswordField
Mypass85
Output
false
Method
public static boolean checkPass(char[] ca){
String s = new String(ca);
System.out.println(s); // Prints : Mypass85
p = Pattern.compile("[A-Za-z]{4}[0-9]{2}");
return p.matcher(s).matches();
}
Matcher#matches attempts to match full input. Use Matcher#find instead:
public static boolean checkPass(String s){
System.out.println(s); // Prints : Mypass85
p = Pattern.compile("[A-Za-z]{4}[0-9]{2}");
return p.matcher(s).find();
}
Promoting a comment to an answer.
It doesn't match because "Mypass85" is 6 letters followed by 2 numbers, but your pattern expects exactly 4 letters followed by 2 numbers.
You can either pass something like "Pass85" to match your existing pattern, or you can get "Mypass85" to match by changing the {4} to {6} or to {4,} (4 or more).

Exclude null regex groups

Given the following line, I would like to extract some values using Pattern class in Java:
user1#machine1:command#user2#machine2:command....
Two commands are extracted:
one to be executed on machine1 using user1
one to be executed on machine2 using user2
If I use the following regex
"([^#]+)#([^:]+):([^#]+)(?:#([^#]+)#([^:]+):([^#]+))*"
the elements in group 1, 4, 7, ... are users
the elements in group 2, 5, 8, ... are machines
the elements in group 3, 6, 9, ... are commands
The only problem is that for only one command, the matcher detects null groups for 4, 5, 6.
Is there any Regex option for not receiving null values, for that particular situation?
Instead of using one regex for finding all the users, groups, and commands at once, I'd suggest splitting the process in two: First, find blocks of user#group:command, then identify the parts in that block. This way it will work for any number of blocks.
First, trim down your regex to match just one "block":
Pattern p = Pattern.compile("([^#]+)#([^:]+):([^#]+)");
String input = "user1#machine1:command1#user2#machine2:command2#user3#machine3:command3";
Then, either, use String.split("#") to split the blocks and use the regex to match that block:
for (String block : input.split("#")) {
Matcher m = p.matcher(block);
if (m.matches()) {
System.out.println(m.groupCount());
for (int i = 0; i < m.groupCount(); i++) {
System.out.println(m.group(i + 1));
}
}
}
Or just repeatedly find more matches in the original string:
Matcher m = p.matcher(input);
while (m.find()) {
System.out.println(m.groupCount());
for (int i = 0; i < m.groupCount(); i++) {
System.out.println(m.group(i + 1));
}
}
Why not just check?
if (myMatcher.find()) {
if (myMatcher.group(4) == null) {
// TODO
}
// etc
I think you have a bigger problem when there are 3 or more commands. You should probably just .split("#") the string first, and then deal with each one individually.

Reading and replacing Integers in a string

I have a string for example "x(10, 9, 8)" I want to read each integer from the string then using the integer as an array index retrieve a new integer from an array and replace it with this value.
All of the methods I've tried seem more suited to applying the same thing to all integers, or just retrieving the integers and then loosing track of them. Can anyone tell me the best way to do this please?
Using regular expressions, you can "browse" through each number in your string, regardless of how they are separated, and replace them as required. For example, the code below prints x(101, 99, 88):
public static void main(String[] args) {
int[] array = {0, 1, 2, 3, 4, 5, 6, 7, 88, 99, 101};
String s = "x(10, 9, 8)";
Pattern p = Pattern.compile("\\d+");
Matcher m = p.matcher(s);
StringBuilder replace = new StringBuilder();
int start = 0;
while(m.find()) {
//append the non-digit part first
replace.append(s.substring(start, m.start()));
start = m.end();
//parse the number and append the number in the array at that index
int index = Integer.parseInt(m.group());
replace.append(array[index]);
}
//append the end of the string
replace.append(s.substring(start, s.length()));
System.out.println(replace);
}
Note: you should add some exception handling.
Parse the numbers of your string using Integer.parseInt(), String.split(","), and String.indexOf() (for the ( and ). Create a List with them.
Iterate through this list and create a new List with the values from the array.
Iterate through the new List and create the response String.

Categories