Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Functional style java.util.regex match/group extraction

Tags:

java

regex

java-9

Using java.util.regex to extract substrings I find myself implementing the same code pattern working around calls to :

Pattern p = Pattern.compile(pattern); // can be static final
Matcher m = p.matcher(input);
if (m.find()) { // or m.matches()
    foo(m.group(x));
} else {
    ...
}

Is there a functional extension or popular library (guava / apache commons) that avoids the ugly unnecessary and error-prone local variable, like:

Pattern p = Pattern.compile(pattern); // can be static final
p.matchedGroup(input, x) // return Optional<String>
    .map(group -> foo(group))
    .orElse(...);

and also a stream of match results like:

Pattern p = Pattern.compile(pattern);
p.findMatches(input)
    .map((MatchResult mr) -> {
        mr.groupsIfPresent(x).map(g -> foo(g)).orElse(...)
    })
    .findFirst();

It seems the only functional addition in Java8 was .splitAsStream() but that only helps when trying to split around matches.

like image 205
tkruse Avatar asked Jun 09 '19 01:06

tkruse


2 Answers

The following is only available from java-9

You're probably looking for Matcher::results which produces a Stream<MatchResult>

You can use it in the following way for example

p.matcher(input)
 .results()
 .map(MatchResult::group)
 .findAny()
 .orElse(null);

An addition added by Holger and which is intersting would be, while reading a File and looking for a pattern to directly use Scanner::findAll to avoid loading the whole file in memory using File::lines

like image 193
Yassin Hajaj Avatar answered Sep 27 '22 17:09

Yassin Hajaj


There is an elegant solution that works for both Stream<String> and Optional<String>:

Pattern pattern = Pattern.compile("...");

List<String> input = new ArrayList<>();

List<String> matches = input.stream()
        .map(pattern::matcher)
        .filter(Matcher::find)
        .map(m -> m.group(x))
        .collect(Collectors.toList());

Though I would like to point out that doing a modifying/mutating operation in a filter-call is unusual. Please be careful when you do mutating operations in filtercalls and avoid them as much as possible. For this case it's fine though (from my subjective viewpoint) as you modify an object you just created in your stream that isn't used outside of the stream operations.

Use Optional.ofNullable/of when you only have one input, everything else looks the same.

like image 34
roookeee Avatar answered Sep 27 '22 15:09

roookeee