Wie kann ich die Anzahl der Übereinstimmungen für eine Regex zählen?

97

Angenommen, ich habe eine Zeichenfolge, die Folgendes enthält:

HelloxxxHelloxxxHello

Ich kompiliere ein Muster, um nach 'Hallo' zu suchen.

Pattern pattern = Pattern.compile("Hello");
Matcher matcher = pattern.matcher("HelloxxxHelloxxxHello");

Es sollte drei Übereinstimmungen finden. Wie kann ich zählen, wie viele Spiele es gab?

Ich habe verschiedene Loops ausprobiert und die verwendet, matcher.groupCount()aber es hat nicht funktioniert.

Tony
quelle
Gibt es eine Möglichkeit, dass Ihre Suchzeichenfolge überlappende Vorkommen in der Eingabezeichenfolge aufweist?
Aioobe

Antworten:

177

matcher.find()findet nicht alle Übereinstimmungen, nur die nächste Übereinstimmung.

Lösung für Java 9+

long matches = matcher.results().count();

Lösung für Java 8 und älter

Sie müssen Folgendes tun. ( Ab Java 9 gibt es eine schönere Lösung )

int count = 0;
while (matcher.find())
    count++;

Übrigens matcher.groupCount()ist etwas ganz anderes.

Vollständiges Beispiel :

import java.util.regex.*;

class Test {
    public static void main(String[] args) {
        String hello = "HelloxxxHelloxxxHello";
        Pattern pattern = Pattern.compile("Hello");
        Matcher matcher = pattern.matcher(hello);

        int count = 0;
        while (matcher.find())
            count++;

        System.out.println(count);    // prints 3
    }
}

Umgang mit überlappenden Übereinstimmungen

Wenn Sie Übereinstimmungen aaim aaaaobigen Snippet zählen, erhalten Sie 2 .

aaaa
aa
  aa

Um 3 Übereinstimmungen zu erhalten, dh dieses Verhalten:

aaaa
aa
 aa
  aa

Sie müssen <start of last match> + 1wie folgt nach einer Übereinstimmung im Index suchen :

String hello = "aaaa";
Pattern pattern = Pattern.compile("aa");
Matcher matcher = pattern.matcher(hello);

int count = 0;
int i = 0;
while (matcher.find(i)) {
    count++;
    i = matcher.start() + 1;
}

System.out.println(count);    // prints 3
aioobe
quelle
Zählen der Anzahl der Übereinstimmungen, die innerhalb der Zeichenfolge auftreten. Die Methode java.util.regex.Matcher.region (int start, int end) legt die Grenzen der Region dieses Matchers fest. Die Region ist der Teil der Eingabesequenz, der durchsucht wird, um eine Übereinstimmung zu finden. Durch Aufrufen dieser Methode wird der Matcher zurückgesetzt und der Bereich so festgelegt, dass er an dem durch den Startparameter angegebenen Index beginnt und an dem durch den Endparameter angegebenen Index endet. Versuche dies. while(matcher.find()){ matcher.region(matcher.end()-1, str.length()); count++; }
Mukesh Kumar Gupta
17

Dies sollte für Übereinstimmungen funktionieren, die sich möglicherweise überschneiden:

public static void main(String[] args) {
    String input = "aaaaaaaa";
    String regex = "aa";
    Pattern pattern = Pattern.compile(regex);
    Matcher matcher = pattern.matcher(input);
    int from = 0;
    int count = 0;
    while(matcher.find(from)) {
        count++;
        from = matcher.start() + 1;
    }
    System.out.println(count);
}
Mary-Anne Wolf
quelle
5

Ab Java 9 können Sie den von bereitgestellten Stream verwenden Matcher.results()

long matches = matcher.results().count();
vương trọng hồ
quelle
3

Wenn Sie Java 8-Streams verwenden möchten und gegen whileSchleifen allergisch sind, können Sie Folgendes versuchen:

public static int countPattern(String references, Pattern referencePattern) {
    Matcher matcher = referencePattern.matcher(references);
    return Stream.iterate(0, i -> i + 1)
            .filter(i -> !matcher.find())
            .findFirst()
            .get();
}

Haftungsausschluss: Dies funktioniert nur bei nicht zusammenhängenden Übereinstimmungen.

Beispiel:

public static void main(String[] args) throws ParseException {
    Pattern referencePattern = Pattern.compile("PASSENGER:\\d+");
    System.out.println(countPattern("[ \"PASSENGER:1\", \"PASSENGER:2\", \"AIR:1\", \"AIR:2\", \"FOP:2\" ]", referencePattern));
    System.out.println(countPattern("[ \"AIR:1\", \"AIR:2\", \"FOP:2\" ]", referencePattern));
    System.out.println(countPattern("[ \"AIR:1\", \"AIR:2\", \"FOP:2\", \"PASSENGER:1\" ]", referencePattern));
    System.out.println(countPattern("[  ]", referencePattern));
}

Dies druckt aus:

2
0
1
0

Dies ist eine Lösung für disjunkte Übereinstimmungen mit Streams:

public static int countPattern(String references, Pattern referencePattern) {
    return StreamSupport.stream(Spliterators.spliteratorUnknownSize(
            new Iterator<Integer>() {
                Matcher matcher = referencePattern.matcher(references);
                int from = 0;

                @Override
                public boolean hasNext() {
                    return matcher.find(from);
                }

                @Override
                public Integer next() {
                    from = matcher.start() + 1;
                    return 1;
                }
            },
            Spliterator.IMMUTABLE), false).reduce(0, (a, c) -> a + c);
}
gil.fernandes
quelle
1

Verwenden Sie den folgenden Code, um die Anzahl der Übereinstimmungen zu ermitteln, die der reguläre Ausdruck in Ihrer Eingabe findet

        Pattern p = Pattern.compile(regex, Pattern.MULTILINE | Pattern.DOTALL);// "regex" here indicates your predefined regex.
        Matcher m = p.matcher(pattern); // "pattern" indicates your string to match the pattern against with
        boolean b = m.matches();
        if(b)
        count++;
        while (m.find())
        count++;

Dies ist jedoch ein verallgemeinerter Code, der nicht spezifisch ist. Passen Sie ihn an Ihre Bedürfnisse an

Bitte zögern Sie nicht, mich zu korrigieren, wenn ein Fehler vorliegt.

sagte amir
quelle