首先,看下面的代码,我以为它三次调用find()方法都会返回true,但实际上并不是。

public class Demo {
    public static void main(String[] args) {
        String str = "hello 123!";
        String regex = "(\\d+)";
        Matcher matcher = Pattern.compile(regex).matcher(str);
        System.out.println(matcher.find());// true
        System.out.println(matcher.find());// false
        System.out.println(matcher.find());// false
    }
}

阅读了Java正则匹配中matcher()和find()的配对问题后认为matcherfind()是一一对应的,但后来发现事实并非如此,看下面的代码:

public class Demo {
    public static void main(String[] args) {
        String str = "hello 123! hello 456! hello 789!";
        String regex = "(\\d+)";
        Matcher matcher = Pattern.compile(regex).matcher(str);
        System.out.println(matcher.find());// true
        System.out.println(matcher.find());// true
        System.out.println(matcher.find());// true
        System.out.println(matcher.find());// false
    }
}

打印的结果分别是:truetruetruefalse。如果按照上面文章的结论应该打印的结果是:truefalsefalsefalse

为什么会这样呢?我们看它的源码,可能会有一点思路:

/**
     * Attempts to find the next subsequence of the input sequence that matches
     * the pattern.
     *
     * <p> This method starts at the beginning of this matcher's region, or, if
     * a previous invocation of the method was successful and the matcher has
     * not since been reset, at the first character not matched by the previous
     * match.
     *
     * <p> If the match succeeds then more information can be obtained via the
     * <tt>start</tt>, <tt>end</tt>, and <tt>group</tt> methods.  </p>
     *
     * @return  <tt>true</tt> if, and only if, a subsequence of the input
     *          sequence matches this matcher's pattern
     */
    public boolean find() {
        int nextSearchIndex = last;
        if (nextSearchIndex == first)
            nextSearchIndex++;

        // If next search starts before region, start it at region
        if (nextSearchIndex < from)
            nextSearchIndex = from;

        // If next search starts beyond region then it fails
        if (nextSearchIndex > to) {
            for (int i = 0; i < groups.length; i++)
                groups[i] = -1;
            return false;
        }
        return search(nextSearchIndex);
    }

将上面的方法注释翻译成中文,如下:

尝试找到与模式匹配的输入序列的下一个子序列。
此方法在此匹配器区域的开头开始,或者,如果该方法的先前调用成功且此后匹配器尚未重置,则在与先前匹配不匹配的第一个字符处开始。
如果匹配成功,则可以通过start 、 end和group方法获得更多信息。
返回:
当且仅当输入序列的子序列与此匹配器的模式匹配时才为真

如果该方法的先前调用成功且此后匹配器尚未重置,则在与先前匹配不匹配的第一个字符处开始。的情况。

我们的测试文本是hello 123! hello 456! hello 789!,字符串中的每一个字符及其下标如下图所示:

Matcher java 如何使用 java matcher.find_Matcher java 如何使用

当我们第一次调用find()方法的时候,一定会搜索到数字的。

Matcher java 如何使用 java matcher.find_Matcher java 如何使用_02

而第二次调用find()方法则从下标为9的字符开始查找起,也能找到与正则表达式相匹配的字符串"456",如下图:

Matcher java 如何使用 java matcher.find_java_03

而第三次调用find()方法则从下标为20的字符开始查找起,也能找到与正则表达式相匹配的字符串"789",如下图:

Matcher java 如何使用 java matcher.find_正则表达式_04

而第四次调用find()方法从下标t为31的字符开查找起,找不到能够与正则表达式相匹配的字符串,返回false。通过debug调试到find()方法内,查看nextSearchIndex局部变量的值跟上面说的一样。

Matcher java 如何使用 java matcher.find_java_05


到此我们应该能够明白第一份代码中find()方法为什么输出的结果是truefalsefalse了。因为find()方法的查找会从上一次匹配成功后不匹配的第一个字符开始查找,而上一份代码整个字符串中只有一份能够匹配,所以后面的调用也就返回false了。

所以对于第一份代码中的问题解决,可以像参考文章中所说的那样,重新再生成一个matcher

public class Demo {
    public static void main(String[] args) {
        String str = "hello 123!";
        String regex = "(\\d+)";
        Matcher matcher = Pattern.compile(regex).matcher(str);
        System.out.println(matcher.find());// true
        matcher = Pattern.compile(regex).matcher(str);
        System.out.println(matcher.find());// true
        matcher = Pattern.compile(regex).matcher(str);
        System.out.println(matcher.find());// true
    }
}

或者在每次调用后再执行reset()方法重置:

public class Demo {
    public static void main(String[] args) {
        String str = "hello 123!";
        String regex = "(\\d+)";
        Matcher matcher = Pattern.compile(regex).matcher(str);
        System.out.println(matcher.find());// true
        matcher.reset();
        System.out.println(matcher.find());// true
        matcher.reset();
        System.out.println(matcher.find());// true
    }
}