Найти последний матч с 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 без циклического перехода по огромному списку?
Ответы
Ответ 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