Issue with finding indices of multiple matches in String with regex - java

I'm attempting to find the indices of multiple matches in a String using Regex (test code below), for use with external libraries.
static String content = "a {non} b {1} c {1}";
static String inline = "\\{[0-9]\\}";
public static void getMatchIndices()
{
Pattern pattern = Pattern.compile(inline);
Matcher matcher = pattern.matcher(content)
while (matcher.find())
{
System.out.println(matcher.group());
Integer i = content.indexOf(matcher.group());
System.out.println(i);
}
}
OUTPUT:
{1}
10
{1}
10
It finds both groups, but returns an index of 10 for both. Any ideas?

From http://docs.oracle.com/javase/1.5.0/docs/api/java/lang/String.html#indexOf(java.lang.String):
Returns the index within this string of the first occurrence of the specified substring.
Since both match the same thing ('{1}') the first occurrence is returned in both cases.
You probably want to use Matcher#start() to determine the start of your match.

You can do this with regexp. The following will find the locations in the string.
static String content = "a {non} b {1} c {1}";
static String inline = "\\{[0-9]\\}";
public static void getMatchIndices()
{
Pattern pattern = Pattern.compile(inline);
Matcher matcher = pattern.matcher(content);
int pos = 0;
while (matcher.find(pos)) {
int found = matcher.start();
System.out.println(found);
pos = found +1;
}
}

Related

capturing group of consecutive digits using regex

i'm trying to capture only the two 6's adjacent to each other and get how many times did it occur using regex like if we had 794234879669786694326666976 the answer should be 2 or if its 66666 it should be zero and so on i'm using the following code and captured it by this (66)* and using matcher.groupcount() to get how many times did it occur but its not working !!!
package me;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class blah {
public static void main(String[] args) {
// Define regex to find the word 'quick' or 'lazy' or 'dog'
String regex = "(66)*";
String text = "6678793346666786784966";
// Obtain the required matcher
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(text);
int match=0;
int groupCount = matcher.groupCount();
System.out.println("Number of group = " + groupCount);
// Find every match and print it
while (matcher.find()) {
match++;
}
System.out.println("count is "+match);
}
}
One approach here would be to use lookarounds to ensure that you match only islands of exactly two sixes:
String regex = "(?<!6)66(?!6)";
String text = "6678793346666786784966";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(text);
This finds a count of two, for the input string you provided (the two matches being the 66 at the very start and end of the string).
The regex pattern uses two lookarounds to assert that what comes before the first 6 and after the second 6 are not other sixes:
(?<!6) assert that what precedes is NOT 6
66 match and consume two 6's
(?!6) assert that what follows is NOT 6
You need to use
String regex = "(?<!6)66(?!6)";
See the regex demo.
Details
(?<!6) - no 6 right before the current location
66 - 66 substring
(?!6) - no 6 right after the current location.
See the Java demo:
String regex = "(?<!6)66(?!6)";
String text = "6678793346666786784966";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(text);
int match=0;
while (matcher.find()) {
match++;
}
System.out.println("count is "+match); // => count is 2
This didn't take long to come up with. I like regular expressions but I don't use them unless really necessary. Here is one loop method that appears to work.
char TARGET = '6';
int GROUPSIZE = 2;
// String with random termination character that's not a TARGET
String s = "6678793346666786784966" + "z";
int consecutiveCount = 0;
int groupCount = 0;
for (char c : s.toCharArray()) {
if (c == TARGET) {
consecutiveCount++;
}
else {
// if current character is not a TARGET, update group count if
// consecutive count equals GROUPSIZE
if (consecutiveCount == GROUPSIZE) {
groupCount++;
}
// in any event, reset consecutive count
consecutiveCount = 0;
}
}
System.out.println(groupCount);

Java Pattern REGEX Where Not Matching

I'm trying to use the Java Pattern and Matcher to apply input checks. I have it working in a really basic format which I am happy with so far. It applies a REGEX to an argument and then loops through the matching characters.
import java.util.regex.Pattern;
import java.util.regex.Matcher;
public class RegexUtil {
public static void main(String[] args) {
String argument;
Pattern pattern;
Matcher matcher;
argument = "#a1^b2";
pattern = Pattern.compile("[a-zA-Z]|[0-9]|\\s");
matcher = pattern.matcher(argument);
// find all matching characters
while(matcher.find()) {
System.out.println(matcher.group());
}
}
}
This is fine for extracting all the good characters, I get the output
a
1
b
2
Now I wanted to know if it's possible to do the same for any characters that don't match the REGEX so I get the output
#
^
Or better yet loop through it and get TRUE or FALSE flags for each index of the argument
false
true
true
false
true
true
I only know how to loop through with matcher.find(), any help would be greatly appreciated
You may add a |(.) alternative to your pattern (to match any char but a line break char) and check if Group 1 matched upon each match. If yes, output false, else, output true:
String argument = "#a1^b2";
Pattern pattern = Pattern.compile("[a-zA-Z]|[0-9]|\\s|(.)"); // or "[a-zA-Z0-9\\s]|(.)"
Matcher matcher = pattern.matcher(argument);
while(matcher.find()) { // find all matching characters
System.out.println(matcher.group(1) == null);
See the Java demo, output:
false
true
true
false
true
true
Note you do not need to use a Pattern.DOTALL here, because \s in your "whitelist" part of the pattern matches line breaks.
Why not simply removing all matching chars from your string, so you get only the non matching ones back:
import java.util.regex.Pattern;
import java.util.regex.Matcher;
public class RegexUtil {
public static void main(String[] args) {
String argument;
Pattern pattern;
Matcher matcher;
argument = "#a1^b2";
pattern = Pattern.compile("[a-zA-Z]|[0-9]|\\s");
matcher = pattern.matcher(argument);
// find all matching characters
while(matcher.find()) {
System.out.println(matcher.group());
argument = argument.replace(matcher.group(), "");
}
System.out.println("argument: " + argument);
}
}
You have to iterate over each char of the String and check one by one :
//for-each loop, shorter way
for (char c : argument.toCharArray()){
System.out.println(pattern.matcher(c + "").matches());
}
or
//classic for-i loop, with details
for (int i = 0; i < argument.length(); i++) {
String charAtI = argument.charAt(i) + "";
boolean doesMatch = pattern.matcher(charAtI).matches();
System.out.println(doesMatch);
}
Also, when you don't require it, you can do declaration and give a value at same time :
String argument = "#a1^b2";
Pattern pattern = Pattern.compile("[a-zA-Z]|[0-9]|\\s");
Track positions, and for each match, print the characters between last match and current match.
int pos = 0;
while (matcher.find()) {
for (int i = pos; i < matcher.start(); i++) {
System.out.println(argument.charAt(i));
}
pos = matcher.end();
}
// Print any trailing characters after last match.
for (int i = pos; i < argument.length(); i++) {
System.out.println(argument.charAt(i));
}
One solution is
String argument;
Pattern pattern;
Matcher matcher;
argument = "#a1^b2";
List<String> charList = Arrays.asList(argument.split(""));
pattern = Pattern.compile("[a-zA-Z]|[0-9]|\\s");
matcher = pattern.matcher(argument);
ArrayList<String> unmatchedCharList = new ArrayList<>();
// find all matching
while(matcher.find()) {
unmatchedCharList.add(matcher.group());
}
for(String charr : charList)
{
System.out.println(unmatchedCharList.contains(charr ));
}
Output
false
true
true
false
true
true

Java regular expression to validate and extract some values

I want to extract all three parts of the following string in Java
MS-1990-10
The first part should always be 2 letters (A-Z)
The second part should always be a year
The third part should always be a number
Does anyone know how can I do that using Java's regular expressions?
You can do this using java's pattern matcher and group syntax:
Pattern datePatt = Pattern.compile("([A-Z]{2})-(\\d{4})-(\\d{2})");
Matcher m = datePatt.matcher("MS-1990-10");
if (m.matches()) {
String g1 = m.group(1);
String g2 = m.group(2);
String g3 = m.group(3);
}
Use Matcher's group so you can get the patterns that actually matched.
In Matcher, the matches inside parenthesis will be captured and can be retrieved via the group() method. To use parenthesis without capturing the matches, use the non-capturing parenthesis (?:xxx).
See also Pattern.
public static void main(String[] args) throws Exception {
String[] lines = { "MS-1990-10", "AA-999-12332", "ZZ-001-000" };
for (String str : lines) {
System.out.println(Arrays.toString(parse(str)));
}
}
private static String[] parse(String str) {
String regex = "";
regex = regex + "([A-Z]{2})";
regex = regex + "[-]";
// regex = regex + "([^0][0-9]+)"; // any year, no leading zero
regex = regex + "([12]{1}[0-9]{3})"; // 1000 - 2999
regex = regex + "[-]";
regex = regex + "([0-9]+)";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(str);
if (!matcher.matches()) {
return null;
}
String[] tokens = new String[3];
tokens[0] = matcher.group(1);
tokens[1] = matcher.group(2);
tokens[2] = matcher.group(3);
return tokens;
}
This is a way to get all 3 parts with a regex:
public class Test {
public static void main(String... args) {
Pattern p = Pattern.compile("([A-Z]{2})-(\\d{4})-(\\d{2})");
Matcher m = p.matcher("MS-1990-10");
m.matches();
for (int i = 1; i <= m.groupCount(); i++)
System.out.println(m.group(i));
}
}
String rule = "^[A-Z]{2}-[1-9][0-9]{3}-[0-9]{2}";
Pattern pattern = Pattern.compile(rule);
Matcher matcher = pattern.matcher(s);
regular matches year between 1000 ~ 9999, u can update as u really need.

Replacing regex with the same amount of "." as its length

See this for my current attempt: http://regexr.com?374vg
I have a regex that captures what I want it to capture, the thing is that the String().replaceAll("regex", ".") replaces everything with just one ., which is fine if it's at the end of the line, but otherwise it doesn't work.
How can I replace every character of the match with a dot, so I get the same amount of . symbols as its length?
Here's a one line solution:
str = str.replaceAll("(?<=COG-\\d{0,99})\\d", ".").replaceAll("COG-(?=\\.+)", "....");
Here's some test code:
String str = "foo bar COG-2134 baz";
str = str.replaceAll("(?<=COG-\\d{0,99})\\d", ".").replaceAll("COG-(?=\\.+)", "....");
System.out.println(str);
Output:
foo bar ........ baz
This is not possible using String#replaceAll. You might be able to use Pattern.compile(regexp) and iterate over the matches like so:
StringBuilder result = new StringBuilder();
Pattern pattern = Pattern.compile(regexp);
Matcher matcher = pattern.matcher(inputString);
int previous = 0;
while (matcher.find()) {
result.append(inputString.substring(previous, matcher.start()));
result.append(buildStringWithDots(matcher.end() - matcher.start()));
previous = matcher.end();
}
result.append(inputString.substring(previous, inputString.length()));
To use this you have to define buildStringWithDots(int length) to build a String containing length dots.
Consider this code:
Pattern p = Pattern.compile("COG-([0-9]+)");
Matcher mt = p.matcher("Fixed. Added ''Show annualized values' chackbox in EF Comp Report. Also fixed the problem with the missing dots for the positions and the problem, described in COG-18613");
if (mt.find()) {
char[] array = new char[mt.group().length()];
Arrays.fill(array, '.');
System.out.println( " <=> " + mt.replaceAll(new String(array)));
}
OUTPUT:
Fixed. Added ''Show annualized values' chackbox in EF Comp Report. Also fixed the problem with the missing dots for the positions and the problem, described in .........
Personally, I'd simplify your life and just do something like this (for starters). I'll let you finish.
public class Test {
public static void main(String[] args) {
String cog = "COG-19708";
for (int i = cog.indexOf("COG-"); i < cog.length(); i++) {
System.out.println(cog.substring(i,i+1));
// build new string
}
}
}
Can you put your regex in grouping so replace it with string that matches the length of matched grouping? Something like:
regex = (_what_i_want_to_match)
String().replaceAll(regex, create string that has that many '.' as length of $1)
?
note: $1 is what you matched in your search
see also: http://www.regular-expressions.info/brackets.html

How to determine where a regex failed to match using Java APIs

I have tests where I validate the output with a regex. When it fails it reports that output X did not match regex Y.
I would like to add some indication of where in the string the match failed. E.g. what is the farthest the matcher got in the string before backtracking. Matcher.hitEnd() is one case of what I'm looking for, but I want something more general.
Is this possible to do?
If a match fails, then Match.hitEnd() tells you whether a longer string could have matched. In addition, you can specify a region in the input sequence that will be searched to find a match. So if you have a string that cannot be matched, you can test its prefixes to see where the match fails:
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class LastMatch {
private static int indexOfLastMatch(Pattern pattern, String input) {
Matcher matcher = pattern.matcher(input);
for (int i = input.length(); i > 0; --i) {
Matcher region = matcher.region(0, i);
if (region.matches() || region.hitEnd()) {
return i;
}
}
return 0;
}
public static void main(String[] args) {
Pattern pattern = Pattern.compile("[A-Z]+[0-9]+[a-z]+");
String[] samples = {
"*ABC",
"A1b*",
"AB12uv",
"AB12uv*",
"ABCDabc",
"ABC123X"
};
for (String sample : samples) {
int lastMatch = indexOfLastMatch(pattern, sample);
System.out.println(sample + ": last match at " + lastMatch);
}
}
}
The output of this class is:
*ABC: last match at 0
A1b*: last match at 3
AB12uv: last match at 6
AB12uv*: last match at 6
ABCDabc: last match at 4
ABC123X: last match at 6
You can take the string, and iterate over it, removing one more char from its end at every iteration, and then check for hitEnd():
int farthestPoint(Pattern pattern, String input) {
for (int i = input.length() - 1; i > 0; i--) {
Matcher matcher = pattern.matcher(input.substring(0, i));
if (!matcher.matches() && matcher.hitEnd()) {
return i;
}
}
return 0;
}
You could use a pair of replaceAll() calls to indicate the positive and negative matches of the input string. Let's say, for example, you want to validate a hex string; the following will indicate the valid and invalid characters of the input string.
String regex = "[0-9A-F]"
String input = "J900ZZAAFZ99X"
Pattern p = Pattern.compile(regex)
Matcher m = p.matcher(input)
String mask = m.replaceAll('+').replaceAll('[^+]', '-')
System.out.println(input)
System.out.println(mask)
This would print the following, with a + under valid characters and a - under invalid characters.
J900ZZAAFZ99X
-+++--+++-++-
If you want to do it outside of the code, I use rubular to test the regex expressions before sticking them in the code.

Categories