Criar matriz de correspondências regex

160

Em Java, estou tentando retornar todas as correspondências de regex para uma matriz, mas parece que você só pode verificar se o padrão corresponde a algo ou não (booleano).

Como posso usar uma correspondência de regex para formar uma matriz de todas as strings que correspondem a uma expressão de regex em uma determinada string?

Jake Sankey
fonte
2
Boa pergunta. As informações que você procura devem fazer parte dos documentos Java sobre Regex e Matcher. Infelizmente, não é.
Cheeso 07/10
3
Uma verdadeira vergonha. Essa funcionalidade parece existir imediatamente em quase todos os outros idiomas (com suporte para expressões regulares).
precisa saber é o seguinte

Respostas:

278

( A resposta do 4castle é melhor que a abaixo, se você puder assumir Java> = 9)

Você precisa criar um correspondente e usá-lo para encontrar correspondências iterativamente.

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

Depois disso, allMatchescontém as correspondências e você pode usar allMatches.toArray(new String[0])para obter uma matriz se realmente precisar de uma.


Você também pode usar MatchResultpara escrever funções auxiliares para fazer loop sobre correspondências, pois Matcher.toMatchResult()retorna uma captura instantânea do estado atual do grupo.

Por exemplo, você pode escrever um iterador lento para permitir que você faça

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

fazendo algo parecido com isto:

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

Com isso,

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

rendimentos

a at 0
b at 1
a at 3
c at 4
a at 5
a at 7
b at 8
a at 10
Mike Samuel
fonte
4
Eu não sugeriria o uso de um ArrayList aqui, pois você não sabe o tamanho inicial e pode querer evitar o redimensionamento do buffer. Em vez disso, prefiro um LinkedList - embora seja apenas uma sugestão e não torne sua resposta menos válida.
Liv
13
@Liv, reserve um tempo para comparar ambos ArrayListe LinkedList, os resultados podem ser surpreendentes.
Anthony Accioly 16/05
Eu ouço o que você está dizendo e estou ciente da velocidade de execução e da pegada de memória nos dois casos; o problema com o ArrayList é que o construtor padrão cria uma capacidade de 10 - se você ultrapassar esse tamanho com chamadas para adicionar ( ), você terá que suportar a alocação de memória e a cópia da matriz - e isso pode acontecer algumas vezes. É verdade que, se você espera apenas algumas correspondências, sua abordagem é a mais eficiente; se, no entanto, você achar que o "redimensionamento" da matriz acontece mais de uma vez, sugiro um LinkedList, ainda mais se você estiver lidando com um aplicativo de baixa latência.
Liv
12
@Liv, se o seu padrão tende a produzir correspondências com um tamanho razoavelmente previsível, e dependendo se o padrão corresponde esparsa ou densamente (com base na soma dos comprimentos de allMatchesvs yourStringHere.length()), provavelmente você pode pré-calcular um bom tamanho para allMatches. Na minha experiência, o custo da LinkedListmemória e da iteração em termos de eficiência geralmente não vale a pena, portanto LinkedListnão é minha postura padrão. Mas, ao otimizar um hot-spot, definitivamente vale a pena trocar as implementações da lista para ver se você obtém uma melhoria.
Mike Samuel
1
No Java 9, agora você pode usar Matcher#resultspara obter um Streamque você pode usar para gerar uma matriz (veja minha resposta ).
4castle 21/10
56

No Java 9, agora você pode usar Matcher#results()para obter um Stream<MatchResult>que você pode usar para obter uma lista / matriz de correspondências.

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())
4castle
fonte
1
Como não há resultados (), execute-o primeiro #
Bravo
14
@ Bravo Você está usando o Java 9? Existe. Eu liguei para a documentação.
4castle
: ((existe alguma alternativa para java 8
logbasex
25

Java torna o regex muito complicado e não segue o estilo perl. Dê uma olhada no MentaRegex para ver como você pode fazer isso em uma única linha de código Java:

String[] matches = match("aa11bb22", "/(\\d+)/g" ); // => ["11", "22"]
TraderJoeChicago
fonte
6
Isso é legal. A barra dupla ainda parece feia, mas acho que não há escapatória disso.
JohnPristine 28/09
mentaregex-0.9.5.jar, 6Kb que salvou meu dia, Obrigado Sérgio!
perfil completo de CONVID19
2
ATENÇÃO! A melhor solução. Use-o!
Vlad Holubiev
13
O site MentaRegex está inoperante? Quando eu visito mentaregex.soliveirajr.com ele só diz "oi"
user64141
1
@ user64141 parece que é #
Amit Gold
11

Aqui está um exemplo simples:

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

(se você tiver mais grupos de captura, poderá consultá-los pelo índice deles como um argumento do método de grupo. Se precisar de uma matriz, use list.toArray())

Bozho
fonte
pattern.matches (input) não funciona. Você precisa passar seu padrão regex (de novo!) -> WTF Java ?! pattern.matches (String regex, Entrada de String); Você quer dizer pattern.matcher (input)?
El Mac
@ElMac Pattern.matches()é um método estático, você não deve chamá-lo em uma Patterninstância. Pattern.matches(regex, input)é simplesmente uma abreviação de Pattern.compile(regex).matcher(input).matches().
dimo414
5

Das trilhas oficiais do 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;
        }

Use finde insira o resultado groupem sua matriz / Lista / qualquer que seja.

Anthony Accioly
fonte
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;
Nikhil Kumar K
fonte