使用Java正则表达式匹配器查找最后一个匹配项

使用Java正则表达式匹配器查找最后一个匹配项,java,regex,Java,Regex,我正在尝试获得比赛的最后一个结果,而不必循环。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); } 这将给我第一个结果。如何找到最后一个匹配项,而不在一个巨大的列表中循环?Java

我正在尝试获得比赛的最后一个结果,而不必循环。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);
}

这将给我第一个结果。如何找到最后一个匹配项,而不在一个巨大的列表中循环?

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

}

我还没有找到一个中断的测试用例。

您可以将
*
预先添加到您的正则表达式中,该正则表达式将使用直到最后一次匹配为止的所有字符:

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));
    }
  }
}
印刷品:

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("([0-9]+) mun");
    Matcher m = p.matcher(new StringBuilder(in).reverse());
    if(m.find()) {
      System.out.println(new StringBuilder(m.group(1)).reverse());
    }
  }
}

但这两种解决方案都不如使用
遍历所有匹配项,而(m.find())
,IMO.

为什么不保持简单呢

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

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

正则表达式是贪婪的:

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

将为最后一次匹配提供一个
匹配器
,您可以通过在“*”前面加上前缀将其应用于大多数正则表达式。当然,如果您不能使用
DOTALL
,您可能需要使用
(?:\d |\d)
或类似于通配符的方法。

这似乎是一种更合理的方法

    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。

要获得最后一个匹配,即使这样也有效,但不确定之前没有提到这一点的原因:

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

使用负前瞻:

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后跟一个空格,并且在其后的任意点上至少有一个数字不带任何空格(num后跟一个空格,并且至少有一个数字)”

通过将其与正面回顾相结合,您可以获得更高的想象力:

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();
}
String in=“num 123 num 1 num 698 num 19238 num 2134”;

Pattern p=Pattern.compile((?与当前接受的答案相比,这个答案并没有使用
“*”
前缀盲目地丢弃列表中的元素。相反,它使用
“(元素分隔符)*(元素)”
使用
。组(2)
。请参阅下面代码中的函数
magic\u last

为了证明这种方法的好处,我还提供了一个函数来选择第n个元素,该元素足够健壮,可以接受少于n个元素的列表。请参见下面代码中的函数
magic

过滤掉“num”文本并只获取数字是留给读者的一个练习(只需在数字模式周围添加一个额外的组:
([0-9]+)
,然后选择第4组而不是第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

只需使用\Z-字符串末尾马赫数

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

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

你能确定它是字符串中的最后一个东西吗?如果是的话,只需使用行尾锚点$
/(num([0-9]+)$/
,但这会转换为java。你可以编写一个递归方法,但我怀疑它是否有意义。是的,我认为这是作弊:-)。将此扩展到一般情况将非常困难。+1表示第二种解决方案,但-1表示您开始使用的讨厌的东西。;)我不想在while中循环的原因(m.find())我正在解析HTML,并且有很多结果。我正在努力使我的代码尽可能高效。我的想法是,不必要地循环整个数组来获取最后一个数组会很慢。Javas regex没有包含这么多结果,这让我感到羞耻。我会尝试一下你的代码。我发现了一个不起作用的极端情况:make由可选部分组成的模式。如果模式的一部分位于二进制搜索的一侧,而第二部分位于另一侧,则搜索将只找到整个模式的一小部分。您的代码没有找到最大匹配项。@KFleischer:在这种情况下,这不是很理想吗?上次出现的
[a]不应该是这样吗+
aaaa
处于索引4,而不是索引0?当您搜索某事物的最后一个索引时,如果最小匹配导致更大的索引,则接受它似乎是合理的。如果您认为它不是期望的行为,也许您可以给出一个具体的示例。您能解释它是什么吗?您是对的!线程startet没有我不需要关于索引的信息,只需要内容。这看起来是真正正确的答案。@KFleischer你确定这有效吗?正则表达式相对于输入没有任何意义string@necromancer这是很久以前的事了,所以我很快就想起来了:所使用的模式是线程启动者说的对他有效的模式,发现t他找到了第一个匹配项。对thread starters代码的唯一更改是使用调查结果的数量来处理最后一组。这很简单,我相信在我写评论的那一天它对我有效。哦,顺便说一下,我意识到你可能误解了
m.groupCount()的语义
--它与找到的匹配项的数量无关。它是正则表达式中有多少个组的计数。在示例代码中,它将始终为1,因为正则表达式中只有1个组。@KFleischer我知道您不是回答的人;)这个答案实际上很奇怪。我把它插入了一个主类中,
的值是
num 123 num 1 num 698 num 19238 num 2134
,哈哈:为什么要在最后用
*$
呢?@ArtOfWarfare,这是不必要的
String in = "num 123 num 1 num 698 num 19238 num 2134";
Pattern p = Pattern.compile("num ([0-9]+)\\Z");
Matcher m = p.matcher(in);

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