Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multiple matches in single java regexp

Tags:

java

regex

Is it possible to match the following in a single regular expression to get the first word, and then a list of the numbers?

this 10 12 3 44 5 66 7 8    # should return "this", "10", "12", ...
another 1 2 3               # should return "another", "1", "2", "3"

EDIT1: My actual data is not this simple, the digits are actually more complex patterns, but for illustration purposes, I've reduced the problem to simple digits, so I do require a regex answer.

The numbers are unknown in length on each line, but all match a simple pattern.

The following only matches "this" and "10":

([\p{Alpha}]+ )(\d+ ?)+?

Dropping the final ? matches "this" and "8".

I had thought that the final group (\d+ ?)+ would do the digit matching multiple times, but it doesn't and I can't find the syntax to do it, if possible.

I can do it in multiple passes, by only searching for the name and latter numbers separately, but was wondering if it's possible in a single expression? (And if not, is there a reason?)


EDIT2: As I mentioned in some of the comments, this was a question in Advent of Code (Day 7, 2020). I was looking to find cleanest solution (who doesn't love a bit of polishing?)

Here's my ultimate solution (kotlin) I used, but spent too long trying to do it in 1 regex, so I posted this question.

val bagExtractor = Regex("""^([\p{Alpha} ]+) bags contain""")
val rulesExtractor = Regex("""([\d]+) ([\p{Alpha} ]+) bag""")

// bagRule is a line from the input
val bag = bagExtractor.find(bagRule)?.destructured!!.let { (n) -> Bag(name = n) }
val contains = rulesExtractor.findAll(bagRule).map { it.destructured.let { (num, bagName) -> Contain(num = num.toInt(), bag = Bag(bagName)) } }.toList()
Rule(bag = bag, contains = contains)

Despite now knowing it can be done in 1 line, I haven't implemented it, as I think it's cleaner in 2.

like image 749
Mark Fisher Avatar asked Sep 18 '26 15:09

Mark Fisher


1 Answers

I think what you are looking for can be achieved by splitting the string on \s+ unless I am missing something.

import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        String str = "this 10 12 3 44 5 66 7 8";
        String[] parts = str.split("\\s+");
        System.out.println(Arrays.toString(parts));
    }
}

Output:

[this, 10, 12, 3, 44, 5, 66, 7, 8]

If you want to select just the alphabetical text and the integer text from the string, you can do it as

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Main {
    public static void main(String[] args) {
        String str = "this 10 12 3 44 5 66 7 8";
        Matcher matcher = Pattern.compile("(\\b\\p{Alpha}+\\b)|(\\b\\d+\\b)").matcher(str);
        while (matcher.find()) {
            System.out.println(matcher.group());
        }
    }
}

Output:

this
10
12
3
44
5
66
7
8

or as

import java.util.List;
import java.util.regex.MatchResult;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

public class Main {
    public static void main(String[] args) {
        String str = "this 10 12 3 44 5 66 7 8";

        List<String> list = Pattern.compile("(\\b\\p{Alpha}+\\b)|(\\b\\d+\\b)")
                            .matcher(str)
                            .results()
                            .map(MatchResult::group)                                                        
                            .collect(Collectors.toList());

        System.out.println(list);
    }
}

Output:

[this, 10, 12, 3, 44, 5, 66, 7, 8]
like image 51
Arvind Kumar Avinash Avatar answered Sep 21 '26 03:09

Arvind Kumar Avinash



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!