Подтвердить что ты не робот

Найти последний матч с Java regex matcher

Я пытаюсь получить последний результат матча, не выполняя цикл .find()

Здесь мой код:

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

Это даст мне первый результат. Как найти совпадение LAST без циклического перехода по огромному списку?

4b9b3361

Ответ 1

Следующее:

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

печатает:

2134

Или это мошенничество?:)

ИЗМЕНИТЬ

Или это также напечатает 2134:

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

Но оба "решения" не лучше, чем просто перебирать все совпадения с помощью while(m.find()), IMO. Возможно, вам захочется сказать, почему вы не хотите этого делать?

Ответ 2

Чтобы получить последний матч, даже это работает, и не уверен, почему это не упоминалось ранее:

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

Ответ 3

Почему бы не сохранить его простым?

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

Ответ 4

Java не обеспечивает такой механизм. Единственное, что я могу предложить, это двоичный поиск последнего индекса.

Это будет примерно так:

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

Изменить

И вот код, который делает это с тех пор, как я обнаружил, что это интересная проблема:

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

}

Я еще не нашел тестового теста.

Ответ 5

Шаблоны Java по умолчанию жадные, это должно сделать следующее.

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

Ответ 6

Используйте отрицательный просмотр:

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

Регулярное выражение читается как "num", за которым следует одно пробел и по меньшей мере одна цифра без каких-либо (число, за которым следует одно пробел и хотя бы одна цифра) в любой точке после него ".

Вы можете стать еще более привлекательным, объединив его с положительным lookbehind:

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

Этот читается как "по крайней мере одна цифра, предшествующая (число и одно пробел), а за ней не следует (число, за которым следует одно пространство и хотя бы одна цифра) в любой точке после него". Таким образом, вам не нужно возиться с группировкой и беспокоиться о потенциальном IndexOutOfBoundsException, выкинутом из Matcher.group(int).

Ответ 7

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

Ответ 8

Регулярные выражения жадные:

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

предоставит вам Matcher для последнего совпадения, и вы можете применить его к большинству регулярных выражений, добавив ". *". Конечно, если вы не можете использовать DOTALL, вы можете использовать (?:\d|\D) или нечто подобное вашему шаблону.

Ответ 9

Это похоже на более равноправный подход.

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

.*? - это неохотное совпадение, поэтому он не будет сожрать все. ?: заставляет группу не захватывать, поэтому внутренняя группа - это группа 1. Согласование кратных по жадному способу приводит к тому, что она соответствует всей строке до тех пор, пока все совпадения не будут исчерпаны, оставив группу 1 со значением вашего последнего совпадения.

Ответ 10

По сравнению с принятым в настоящее время ответом, он не слепо отбрасывает элементы списка с помощью префикса ".*". Вместо этого он использует "(element delimiter)*(element)" для выбора последнего элемента с помощью .group(2). См. Функцию magic_last в коде ниже.

Чтобы продемонстрировать преимущество этого подхода, я также включил функцию для выбора n-го элемента, который достаточно прочен, чтобы принять список, содержащий менее n элементов. См. Функцию magic в коде ниже.

Фильтрация текста "num", и только получение номера остается в качестве упражнения для читателя (просто добавьте дополнительную группу вокруг шаблона цифр: ([0-9]+) и выберите группу 4 вместо группы 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));
    }
  }

}

Вывод:

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