Utwórz tablicę dopasowań wyrażeń regularnych


160

W Javie próbuję zwrócić wszystkie dopasowania wyrażeń regularnych do tablicy, ale wydaje się, że możesz tylko sprawdzić, czy wzorzec pasuje do czegoś, czy nie (boolean).

Jak mogę użyć dopasowania wyrażenia regularnego, aby utworzyć tablicę wszystkich ciągów pasujących do wyrażenia regularnego w danym ciągu?


2
Dobre pytanie. Informacje, których szukasz, powinny znajdować się w dokumentacji Java dotyczącej Regex i Matcher. Niestety tak nie jest.
Cheeso

3
Szkoda. Ta funkcjonalność wydaje się istnieć od razu w prawie każdym innym języku (który obsługuje wyrażenia regularne).
Ray Toal

Odpowiedzi:


278

( Odpowiedź 4castle jest lepsza niż poniżej, jeśli możesz założyć, że Java> = 9)

Musisz utworzyć dopasowanie i użyć go do iteracyjnego znajdowania dopasowań.

 import java.util.regex.Matcher;
 import java.util.regex.Pattern;

 ...

 List<String> allMatches = new ArrayList<String>();
 Matcher m = Pattern.compile("your regular expression here")
     .matcher(yourStringHere);
 while (m.find()) {
   allMatches.add(m.group());
 }

Następnie allMatcheszawiera dopasowania i możesz użyć allMatches.toArray(new String[0])do pobrania tablicy, jeśli naprawdę jej potrzebujesz.


Możesz również użyć MatchResultdo napisania funkcji pomocniczych do zapętlenia dopasowań, ponieważ Matcher.toMatchResult()zwraca migawkę bieżącego stanu grupy.

Na przykład możesz napisać leniwy iterator, który pozwoli ci to zrobić

for (MatchResult match : allMatches(pattern, input)) {
  // Use match, and maybe break without doing the work to find all possible matches.
}

robiąc coś takiego:

public static Iterable<MatchResult> allMatches(
      final Pattern p, final CharSequence input) {
  return new Iterable<MatchResult>() {
    public Iterator<MatchResult> iterator() {
      return new Iterator<MatchResult>() {
        // Use a matcher internally.
        final Matcher matcher = p.matcher(input);
        // Keep a match around that supports any interleaving of hasNext/next calls.
        MatchResult pending;

        public boolean hasNext() {
          // Lazily fill pending, and avoid calling find() multiple times if the
          // clients call hasNext() repeatedly before sampling via next().
          if (pending == null && matcher.find()) {
            pending = matcher.toMatchResult();
          }
          return pending != null;
        }

        public MatchResult next() {
          // Fill pending if necessary (as when clients call next() without
          // checking hasNext()), throw if not possible.
          if (!hasNext()) { throw new NoSuchElementException(); }
          // Consume pending so next call to hasNext() does a find().
          MatchResult next = pending;
          pending = null;
          return next;
        }

        /** Required to satisfy the interface, but unsupported. */
        public void remove() { throw new UnsupportedOperationException(); }
      };
    }
  };
}

Z tym,

for (MatchResult match : allMatches(Pattern.compile("[abc]"), "abracadabra")) {
  System.out.println(match.group() + " at " + match.start());
}

plony

a at 0
b at 1
a at 3
c at 4
a at 5
a at 7
b at 8
a at 10

4
Nie sugerowałbym używania tutaj ArrayList, ponieważ nie znasz z góry rozmiaru i możesz chcieć uniknąć zmiany rozmiaru bufora. Zamiast tego wolałbym LinkedList - chociaż to tylko sugestia i nie sprawia, że ​​twoja odpowiedź jest mniej ważna.
Liv

13
@Liv, trochę czasu, aby zarówno odniesienia ArrayListi LinkedListwyniki mogą być zaskakujące.
Anthony Accioly

Słyszę, co mówisz, i jestem świadomy szybkości wykonywania i zużycia pamięci w obu przypadkach; problem z ArrayList polega na tym, że domyślny konstruktor tworzy pojemność 10 - jeśli przekroczysz ten rozmiar z wywołaniami do dodania ( ) będziesz musiał poradzić sobie z alokacją pamięci i kopiowaniem tablicy - i może się to zdarzyć kilka razy. Oczywiście, jeśli spodziewasz się tylko kilku dopasowań, twoje podejście jest bardziej efektywne; jeśli jednak okaże się, że zmiana rozmiaru tablicy ma miejsce więcej niż raz, proponuję LinkedList, tym bardziej, jeśli masz do czynienia z aplikacją o niskim opóźnieniu.
Liv

12
@Liv, Jeśli twój wzorzec ma tendencję do tworzenia dopasowań o dość przewidywalnym rozmiarze i w zależności od tego, czy wzór pasuje rzadko, czy gęsto (na podstawie sumy długości allMatchesvs yourStringHere.length()), prawdopodobnie możesz wstępnie obliczyć dobry rozmiar allMatches. Z mojego doświadczenia LinkedListwynika , że koszt pamięci i wydajności iteracji zwykle nie jest tego wart, więc LinkedListnie jest to moja domyślna postawa. Ale podczas optymalizacji punktu aktywnego zdecydowanie warto zamienić implementacje list, aby zobaczyć, czy uzyskasz poprawę.
Mike Samuel

1
W Javie 9 możesz teraz użyć, Matcher#resultsaby uzyskać, Streamktórego możesz użyć do wygenerowania tablicy (zobacz moją odpowiedź ).
4castle

56

W Javie 9 możesz teraz użyć, Matcher#results()aby uzyskać Stream<MatchResult>listę / tablicę dopasowań.

import java.util.regex.Pattern;
import java.util.regex.MatchResult;
String[] matches = Pattern.compile("your regex here")
                          .matcher("string to search from here")
                          .results()
                          .map(MatchResult::group)
                          .toArray(String[]::new);
                    // or .collect(Collectors.toList())

1
ich metoda nie ma wyników (), uruchom tę najpierw
Bravo

14
@Bravo Czy używasz Java 9? Ona istnieje. Podlinkowałem do dokumentacji.
4castle

: ((czy istnieje alternatywa dla java 8
logbasex

25

Java sprawia, że ​​regex jest zbyt skomplikowany i nie jest zgodny ze stylem perla. Spójrz na MentaRegex, aby zobaczyć, jak możesz to osiągnąć w jednej linii kodu Java:

String[] matches = match("aa11bb22", "/(\\d+)/g" ); // => ["11", "22"]

6
To super. Podwójne cięcie nadal wygląda brzydko, ale wydaje mi się, że nie ma w tym żadnego efektu.
JohnPristine,

mentaregex-0.9.5.jar, 6Kb to uratowało mi dzień, Obrigado Sérgio!
CONvid19

2
UWAGA! Najlepszym rozwiązaniem. Użyj tego!
Vlad Holubiev

14
Czy witryna MentaRegex nie działa? Kiedy odwiedzam stronę mentaregex.soliveirajr.com , mówi tylko „cześć”
user64141

1
@ user64141 wygląda na to, że jest
Amit Gold

11

Oto prosty przykład:

Pattern pattern = Pattern.compile(regexPattern);
List<String> list = new ArrayList<String>();
Matcher m = pattern.matcher(input);
while (m.find()) {
    list.add(m.group());
}

(jeśli masz więcej grup przechwytywania, możesz odwołać się do nich przez ich indeks jako argument metody group. Jeśli potrzebujesz tablicy, użyj list.toArray())


pattern.matches (input) nie działa. Musisz przekazać swój wzorzec wyrażenia regularnego (ponownie!) -> WTF Java ?! pattern.matches (ciąg wyrażeń regularnych, ciąg znaków wejściowych); Czy masz na myśli pattern.matcher (wejście)?
El Mac

@ElMac Pattern.matches()to metoda statyczna, nie należy jej wywoływać w Patterninstancji. Pattern.matches(regex, input)jest po prostu skrótem dla Pattern.compile(regex).matcher(input).matches().
dimo414

5

Z oficjalnych tras Regex Java :

        Pattern pattern = 
        Pattern.compile(console.readLine("%nEnter your regex: "));

        Matcher matcher = 
        pattern.matcher(console.readLine("Enter input string to search: "));

        boolean found = false;
        while (matcher.find()) {
            console.format("I found the text \"%s\" starting at " +
               "index %d and ending at index %d.%n",
                matcher.group(), matcher.start(), matcher.end());
            found = true;
        }

Użyj findi wstaw wynik groupdo swojej tablicy / listy / cokolwiek.


0
        Set<String> keyList = new HashSet();
        Pattern regex = Pattern.compile("#\\{(.*?)\\}");
        Matcher matcher = regex.matcher("Content goes here");
        while(matcher.find()) {
            keyList.add(matcher.group(1)); 
        }
        return keyList;
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.