3

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?

2
  • I'm not entirely sure what you're after, but it doesn't seem like varargs and lambda can mix in the way you want. In order to use a lambda, the target type must be a functional interface, whose single method has a fixed number of arguments, determined at compile time. The root of the problem is that it isn't known until runtime how many groups are in the regexp. If there were only one group, how would processData be called, as it seems to require two args? Commented Nov 23, 2013 at 9:58
  • Actually, I think, the caller does know how many groups are in regexp and the idea with method overrides might work. Just not with a single method. Commented Nov 25, 2013 at 19:41

2 Answers 2

2

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
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for the solution in Scala. Did not know this trick. I will try to see if there is some other solution possible in Java 8. Not tuple initialization, but maybe overrides with 1,2 or 3 parameters and then resorting to array for more. It will cover a lot of simple parsing cases.
think making overrides is the way to go, i was about to code some skeleton code, but got a little lazy. (i was also wondering how flexible java's new type inference system is).
2

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;
    }

}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.