1

i'm currently making a regex generator for some invoices, and I wanted to know if there is a way to know that my regular expression matched with one and only one part of the text. The regular expr itself is not really important, I just want to know if I can check there was only one match and no more. I'm of course using Pattern and Matcher from java.util.regex package. I tried to use matcher.groupCount() but that doesnt seem to do the trick because it involves group count not the matches count. Thanks in advance for your answers.

R113
  • 11
  • 1

2 Answers2

0

You could just use String#matches here with some lookahead logic:

String input = "The quick brown fox jumps over the lazy dog.";
if (input.matches("((?!\\bfox\\b).)*\\bfox\\b(?!.*\\bfox\\b).*")) {
    System.out.println("MATCH");
}

The above regex being used is (String#matches uses implicit starting/ending anchors):

^((?!\bfox\b).)*\bfox\b(?!.*\bfox\b).*$

This will match a single first occurrence of fox, provided that fox does not occur anywhere else later in the input string.

Edit:

The above answer can be made to work across lines by enabling dot all mode using the (?s) flag:

String input = "The quick brown\nfox jumps \tover the lazy dog.";
if (input.matches("(?s)((?!\\bfox\\b).)*\\bfox\\b(?!.*\\bfox\\b).*")) {
    System.out.println("MATCH");
}
Tim Biegeleisen
  • 502,043
  • 27
  • 286
  • 360
  • Thanks for your answer, would this work on a multiline text ? I'm getting input from a large file and i'm searching amounts, In some case there is exactly one match and my purpose is to determine when. – R113 Apr 06 '20 at 15:09
  • @R113 Yes, just add `(?s)` to the start of the regex pattern using in `String#matches`, and then it should still work in multiline mode. – Tim Biegeleisen Apr 06 '20 at 15:13
0

Just iterate the matches and keep a count.

final Matcher matcher = Pattern.compile("[fm]oo").matcher(",foo,moo,");

int numMatches = 0;
String match = null;
while (matcher.find()) {
    numMatches++;
    match = matcher.group();
}

if (numMatches > 1) {
    System.out.println("expected one match, found " + numMatches);
}
else {
    System.out.println(match);
}

If you don't care how many matches there are, you can put a condition in the loop and break as soon as you've found the 2nd one.

Or refactor to a util method. In this implementation, I presumed you didn't care about how many matches there are.

public static void main(String[] args) {
    final Matcher matcher = Pattern.compile("[fm]oo").matcher(",foo,moo,");

    System.out.println(
        getSingleMatch(matcher)
             .orElseThrow(() -> new RuntimeException("Expected exactly 1 match"))
             //or handle the failure however use you like
    );
}

private static Optional<String> getSingleMatch(Matcher matcher) {
    final boolean foundOnce = matcher.find();
    if (!foundOnce) return Optional.empty();
    final String firstMatch = matcher.group();
    final boolean foundTwice = matcher.find();
    return !foundTwice ? Optional.ofNullable(firstMatch) : Optional.empty();
}
Michael
  • 41,989
  • 11
  • 82
  • 128