Reputation: 9706
I would like to write a function, that can match a string against regex and execute a callback with all group matches as parameters.
I came up with this and it works:
private static void parse(String source, String regex,
Consumer<String[]> callback) {
//convert regex groups 1..n into integer array 0..n-1 and call
//consumer callback with this array
Matcher m = Pattern.compile(regex).matcher(source);
String[] ret = new String[m.groupCount()];
if (m.matches()) {
for (int i=0; i<m.groupCount(); i++) {
ret[i] = m.group(1+i);
}
callback.accept(ret);
}
}
You can then do
parse("Add 42,43", "Add (\\d+?),(\\d+?)", p -> processData(p[0],p[1]));
What I would like to be able to do ideally is this
parse("Add 42,43", "Add (\\d+?),(\\d+?)", (x,y) -> processData(x,y));
What would be the most elegant way? The only one I can think of is to declare multiple functional interfaces with 1..n parameters and use overrides to handle it. Any better ideas, maybe with reflection?
Upvotes: 3
Views: 4393
Reputation: 9706
Since I solved it for myself by now, I will post a solution I came up with here. If someone proposes a better one, maybe with method chaining or more generic, I will gladly grant an answer.
You can use the class below like this:
Sring msg = "add:42,34";
ParseUtils.parse(msg, "add:(\\d+),(\\d+)", (int x,int y) -> simulator.add(x, y));
ParseUtils.parse(msg, "add:(\\d+),(\\d+)", simulator::add); //IntBiConsumer match
ParseUtils.parse(msg, "add:(.*?),", System.out::println);
And here is the class (I omitted trivial error processing and boolean returns if no match):
public class ParseUtils {
@FunctionalInterface
public interface Consumer { void accept(String s); }
@FunctionalInterface
public interface BiConsumer { void accept(String a, String b); }
//... you can add TriConsumer etc. if you need to ...
@FunctionalInterface //conveniently parses integers
public interface IntBiConsumer { void accept(int x, int y); }
// implementations -----
public static void parse(String src, String regex, Consumer callback) {
callback.accept(parse(src, regex)[0]);
}
public static void parse(String src, String regex, BiConsumer callback) {
String[] p = parse(src, regex);
callback.accept(p[0],p[1]);
}
public static void parse(String src, String regex,
IntBiConsumer callback) {
String[] p = parse(src, regex);
callback.accept(Integer.parseInt(p[0]), Integer.parseInt(p[1]));
}
public static String[] parse(String source, String pattern) {
Pattern p = Pattern.compile(pattern);
Matcher m = p.matcher(source);
String[] ret = new String[m.groupCount()];
if (m.matches()) {
for (int i=0; i<m.groupCount(); i++) {
ret[i] = m.group(1 + i);
}
}
return ret;
}
}
Upvotes: 2
Reputation: 16516
As I understand the question is if there is a syntax sugar for tuple initialization from an array, i.e.:
val (hour, minutes, seconds) = new String[]{"12", "05", "44"};
... except for it is hidden inside a lambda arguments declaration.
As far as I know, there is no such syntax in Java 8 and your approach seems the most convenient. There is in Scala, however: Is there a way to initialize multiple variables from array or List in Scala?.
There are similar instructions in Scala as well:
scala> val s = "Add 42,43"
scala> val r = "Add (\\d+?),(\\d+?)".r
scala> val r(x,y) = s
x: String = 42
y: String = 43
Upvotes: 2