Crea una serie di corrispondenze regex


160

In Java, sto cercando di restituire tutte le corrispondenze regex a un array, ma sembra che sia possibile verificare solo se il pattern corrisponde a qualcosa (booleano).

Come posso usare una corrispondenza regex per formare una matrice di tutte le stringhe che corrispondono a un'espressione regex in una determinata stringa?


2
Buona domanda. Le informazioni che cerchi dovrebbero far parte dei documenti Java su Regex e Matcher. Purtroppo no.
Cheeso,

3
Un vero peccato. Questa funzionalità sembra essere pronta all'uso in quasi tutte le altre lingue (con supporto per le espressioni regolari).
Ray Toal,

Risposte:


278

( La risposta di 4castle è migliore di quella seguente se puoi assumere Java> = 9)

Devi creare un matcher e usarlo per trovare iterativamente corrispondenze.

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

Dopo questo, allMatchescontiene le corrispondenze e puoi usare allMatches.toArray(new String[0])per ottenere un array se ne hai davvero bisogno.


Puoi anche usare MatchResultper scrivere le funzioni di supporto per ripetere le partite poiché Matcher.toMatchResult()restituisce un'istantanea dello stato corrente del gruppo.

Ad esempio puoi scrivere un iteratore pigro per farti fare

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

facendo qualcosa del genere:

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

Con questo,

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

i rendimenti

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
Non consiglierei di usare un ArrayList qui poiché non conosci anticipatamente le dimensioni e potresti voler evitare il ridimensionamento del buffer. Preferirei invece un LinkedList, anche se è solo un suggerimento e non rende la tua risposta meno valida.
Liv

13
@Liv, prenditi il ​​tempo per fare il benchmark di entrambi ArrayListe LinkedList, i risultati potrebbero essere sorprendenti.
Anthony Accioly,

Ho sentito quello che stai dicendo e sono consapevole della velocità di esecuzione e dell'ingombro della memoria in entrambi i casi; il problema con ArrayList è che il costruttore predefinito crea una capacità di 10 - se superi quella dimensione con le chiamate da aggiungere ( ) dovrai occuparti dell'allocazione della memoria e della copia dell'array e ciò potrebbe accadere alcune volte. Certo, se ti aspetti solo alcune partite, il tuo approccio è quello più efficiente; se tuttavia scopri che l'array "ridimensionamento" si verifica più di una volta, ti suggerirei un LinkedList, ancor più se hai a che fare con un'app a bassa latenza.
Liv

12
@Liv, se il tuo modello tende a produrre corrispondenze con una dimensione abbastanza prevedibile e in base al fatto che il modello corrisponda scarsamente o densamente (in base alla somma delle lunghezze di allMatchesvs yourStringHere.length()), puoi probabilmente pre-calcolare una buona dimensione per allMatches. Nella mia esperienza, il costo della LinkedListmemoria e dell'iterazione in termini di efficienza di solito non ne vale la pena, quindi LinkedListnon è la mia postura predefinita. Ma quando si ottimizza un hot-spot, vale sicuramente la pena scambiare le implementazioni dell'elenco per vedere se si ottiene un miglioramento.
Mike Samuel,

1
In Java 9, ora puoi usare Matcher#resultsper ottenere un Streamche puoi usare per generare un array (vedi la mia risposta ).
4castle,

56

In Java 9, ora è possibile utilizzare Matcher#results()per ottenere un Stream<MatchResult>che è possibile utilizzare per ottenere un elenco / array di corrispondenze.

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
il loro non è il metodo results (), esegui prima questo
Bravo

14
@Bravo Stai usando Java 9? Esiste. Ho collegato alla documentazione.
4castle

: ((c'è qualche alternativa a java 8
logbasex

25

Java rende regex troppo complicato e non segue lo stile perl. Dai un'occhiata a MentaRegex per vedere come puoi farlo in una singola riga di codice Java:

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

6
Questo è figo. La doppia barra sembra ancora brutta, ma immagino che non ci sia scape.
JohnPristine,

mentaregex-0.9.5.jar, 6Kb che mi ha salvato la giornata, Obrigado Sérgio!
CONvid19,

2
ATTENZIONE! La migliore soluzione. Usalo!
Vlad Holubiev,

14
Il sito MentaRegex è inattivo? Quando visito mentaregex.soliveirajr.com dice solo "ciao"
user64141

1
@ user64141 sembra che sia
Amit Gold,

11

Ecco un semplice esempio:

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

(se si dispone di più gruppi di acquisizione, è possibile fare riferimento a essi dal loro indice come argomento del metodo di gruppo. Se è necessario un array, utilizzare list.toArray())


pattern.matches (input) non funziona. Devi passare il tuo modello regex (di nuovo!) -> WTF Java ?! pattern.matches (String regex, Input stringa); Intendi pattern.matcher (input)?
El Mac

@ElMac Pattern.matches()è un metodo statico, non dovresti chiamarlo su Patternun'istanza. Pattern.matches(regex, input)è semplicemente una scorciatoia per Pattern.compile(regex).matcher(input).matches().
dimo414,

5

Dalle tracce ufficiali 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;
        }

Usa finde inserisci il risultante groupnel tuo array / Elenco / qualunque cosa.


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;
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.