web-dev-qa-db-fra.com

Trouver le dernier match avec Java regex matcher

J'essaie d'obtenir le dernier résultat d'un match sans avoir à passer en revue .find ()

Voici mon code:

String in = "num 123 num 1 num 698 num 19238 num 2134";
Pattern p = Pattern.compile("num '([0-9]+) ");
Matcher m = p.matcher(in);

if (m.find()) {
     in = m.group(1);
}

Cela me donnera le premier résultat. Comment trouver le DERNIER match sans parcourir une liste potentiellement volumineuse?

19
kireol

Vous pouvez ajouter .* à votre regex, ce qui grelyily consommera tous les caractères jusqu'à la dernière correspondance:

import Java.util.regex.*;

class Test {
  public static void main (String[] args) {
    String in = "num 123 num 1 num 698 num 19238 num 2134";
    Pattern p = Pattern.compile(".*num ([0-9]+)");
    Matcher m = p.matcher(in);
    if(m.find()) {
      System.out.println(m.group(1));
    }
  }
}

Impressions:

2134

Vous pouvez également inverser la chaîne et modifier votre expression rationnelle pour qu'elle corresponde à l'inverse:

import Java.util.regex.*;

class Test {
  public static void main (String[] args) {
    String in = "num 123 num 1 num 698 num 19238 num 2134";
    Pattern p = Pattern.compile("([0-9]+) mun");
    Matcher m = p.matcher(new StringBuilder(in).reverse());
    if(m.find()) {
      System.out.println(new StringBuilder(m.group(1)).reverse());
    }
  }
}

Mais aucune solution n'est meilleure que de simplement parcourir toutes les correspondances avec while (m.find()), IMO.

16
Bart Kiers

Pour obtenir le dernier match, même cela fonctionne et vous ne savez pas pourquoi cela n'a pas été mentionné précédemment:

String in = "num 123 num 1 num 698 num 19238 num 2134";
Pattern p = Pattern.compile("num '([0-9]+) ");
Matcher m = p.matcher(in);
if (m.find()) {
  in= m.group(m.groupCount());
}
14
araut

Pourquoi ne pas garder les choses simples?

in.replaceAll(".*[^\\d](\\d+).*", "$1")
5
Garrett Hall

Java ne fournit pas un tel mécanisme. La seule chose que je peux suggérer serait une recherche binaire du dernier index.

Ce serait quelque chose comme ça:

N = haystack.length();
if ( matcher.find(N/2) ) {
    recursively try right side
else
    recursively try left side

Modifier

Et voici le code qui le fait puisque j'ai trouvé qu'il s'agissait d'un problème intéressant:

import org.junit.Test;

import Java.util.regex.Matcher;
import Java.util.regex.Pattern;

import static org.junit.Assert.assertEquals;

public class RecursiveFind {
    @Test
    public void testFindLastIndexOf() {
        assertEquals(0, findLastIndexOf("abcdddddd", "abc"));
        assertEquals(1, findLastIndexOf("dabcdddddd", "abc"));
        assertEquals(4, findLastIndexOf("aaaaabc", "abc"));
        assertEquals(4, findLastIndexOf("aaaaabc", "a+b"));
        assertEquals(6, findLastIndexOf("aabcaaabc", "a+b"));
        assertEquals(2, findLastIndexOf("abcde", "c"));
        assertEquals(2, findLastIndexOf("abcdef", "c"));
        assertEquals(2, findLastIndexOf("abcd", "c"));
    }

    public static int findLastIndexOf(String haystack, String needle) {
        return findLastIndexOf(0, haystack.length(), Pattern.compile(needle).matcher(haystack));
    }

    private static int findLastIndexOf(int start, int end, Matcher m) {
        if ( start > end ) {
            return -1;
        }

        int pivot = ((end-start) / 2) + start;
        if ( m.find(pivot) ) {
            //recurse on right side
            return findLastIndexOfRecurse(end, m);
        } else if (m.find(start)) {
            //recurse on left side
            return findLastIndexOfRecurse(pivot, m);
        } else {
            //not found at all between start and end
            return -1;
        }
    }

    private static int findLastIndexOfRecurse(int end, Matcher m) {
        int foundIndex = m.start();
        int recurseIndex = findLastIndexOf(foundIndex + 1, end, m);
        if ( recurseIndex == -1 ) {
            return foundIndex;
        } else {
            return recurseIndex;
        }
    }

}

Je n'ai pas encore trouvé de cas de test de rupture.

3
Mark Peters

Les modèles Java sont gourmands par défaut, ce qui suit devrait le faire.

    String in = "num 123 num 1 num 698 num 19238 num 2134";
    Pattern p = Pattern.compile( ".*num ([0-9]+).*$" );
    Matcher m = p.matcher( in );

    if ( m.matches() )
    {
        System.out.println( m.group( 1 ));
    }
2
krico

Utilisez la recherche négative:

String in = "num 123 num 1 num 698 num 19238 num 2134";
Pattern p = Pattern.compile("num (\\d+)(?!.*num \\d+)");
Matcher m = p.matcher(in);

if (m.find()) {
    in= m.group(1);
}

L'expression régulière se lit comme suit: "num suivi d'un espace et d'au moins un chiffre sans aucun chiffre (num suivi d'un espace et d'au moins un chiffre) à n'importe quel point après".

Vous pouvez obtenir encore plus d'amateurs en le combinant avec une apparence positive:

String in = "num 123 num 1 num 698 num 19238 num 2134";
Pattern p = Pattern.compile("(?<=num )\\d+(?!.*num \\d+)");
Matcher m = p.matcher(in);

if (m.find()) {
    in = m.group();
}

Cela se lit comme "au moins un chiffre précédé de (num et un espace) et non suivi de (num suivi d'un espace et d'au moins un chiffre) en aucun point après" . De cette façon, vous n'avez pas gâcher avec le groupement et s'inquiéter de la IndexOutOfBoundsException jeté de Matcher.group(int).

2
dhalsim2
String in = "num 123 num 1 num 698 num 19238 num 2134";  
Pattern p = Pattern.compile("num '([0-9]+) ");  
Matcher m = p.matcher(in);  
String result = "";

while (m.find())
{
     result = m.group(1);
}
1
Norman Seßler

Les expressions régulières sont gourmandes:

Matcher m=Pattern.compile(".*num '([0-9]+) ",Pattern.DOTALL).matcher("num 123 num 1 num 698 num 19238 num 2134");

vous donnera une Matcher pour le dernier match, et vous pouvez l’appliquer à la plupart des regex en ajoutant ". *" Bien sûr, si vous ne pouvez pas utiliser DOTALL, vous pouvez utiliser (?:\d|\D) ou un nom similaire à votre caractère générique.

0
yingted

Par rapport à la réponse actuellement acceptée, celle-ci ne supprime pas aveuglément des éléments de la liste en utilisant le préfixe".*". Au lieu de cela, il utilise "(element delimiter)*(element)" pour choisir le dernier élément en utilisant .group(2). Voir la fonction magic_last dans le code ci-dessous.

Pour démontrer l'intérêt de cette approche, j'ai également inclus une fonction permettant de sélectionner le n-ème élément suffisamment robuste pour accepter une liste comportant moins de n éléments. Voir la fonction magic dans le code ci-dessous.

Filtrer le texte "num" et obtenir uniquement le numéro reste comme exercice pour le lecteur (ajoutez simplement un groupe supplémentaire autour du modèle de chiffres: ([0-9]+) et choisissez le groupe 4 au lieu du groupe 2).

package com.example;

import static Java.lang.System.out;

import Java.util.regex.Matcher;
import Java.util.regex.Pattern;

public class Foo {

  public static void main (String [] args) {
    String element = "num [0-9]+";
    String delimiter = ", ";
    String input;
    input = "here is a num bro: num 001; hope you like it";
    magic_last(input, element, delimiter);
    magic(1, input, element, delimiter);
    magic(2, input, element, delimiter);
    magic(3, input, element, delimiter);
    input = "here are some nums bro: num 001, num 002, num 003, num 004, num 005, num 006; hope you like them";
    magic_last(input, element, delimiter);
    magic(1, input, element, delimiter);
    magic(2, input, element, delimiter);
    magic(3, input, element, delimiter);
    magic(4, input, element, delimiter);
    magic(5, input, element, delimiter);
    magic(6, input, element, delimiter);
    magic(7, input, element, delimiter);
    magic(8, input, element, delimiter);
  }

  public static void magic_last (String input, String element, String delimiter) {
    String regexp = "(" + element + delimiter + ")*(" + element + ")";
    Pattern pattern = Pattern.compile(regexp);
    Matcher matcher = pattern.matcher(input);
    if (matcher.find()) {
        out.println(matcher.group(2));
    }
  }

  public static void magic (int n, String input, String element, String delimiter) {
    String regexp = "(" + element + delimiter + "){0," + (n - 1) + "}(" + element + ")(" + delimiter + element + ")*";
    Pattern pattern = Pattern.compile(regexp);
    Matcher matcher = pattern.matcher(input);
    if (matcher.find()) {
        out.println(matcher.group(2));
    }
  }

}

Sortie:

num 001
num 001
num 001
num 001
num 006
num 001
num 002
num 003
num 004
num 005
num 006
num 006
num 006
0
necromancer

Cela semble être une approche plus également plausible.

    public class LastMatchTest {
        public static void main(String[] args) throws Exception {
            String target = "num 123 num 1 num 698 num 19238 num 2134";
            Pattern regex = Pattern.compile("(?:.*?num.*?(\\d+))+");
            Matcher regexMatcher = regex.matcher(target);

            if (regexMatcher.find()) {
                System.out.println(regexMatcher.group(1));
            }
        }
    }

Le .*? est une correspondance réticente, il ne va donc pas tout engloutir. Le ?: force un groupe sans capture de sorte que le groupe interne soit le groupe 1. La correspondance de multiples de manière gourmande le fait correspondre sur toute la chaîne jusqu'à épuisement des correspondances, laissant le groupe 1 avec la valeur de votre dernière correspondance.

0
Bradley M Handy