如何使用Java API确定正则表达式匹配失败的位置

20

我有一些测试,其中使用正则表达式验证输出。当测试失败时,会报告输出X未匹配正则表达式Y。

我想要添加一些指示,用于指出匹配失败的字符串位置。例如,在回溯之前匹配器在字符串中最远到达的位置。Matcher.hitEnd()是我所寻找的情况之一,但我需要更通用的方法。

这个是否可能做到?


1
这可能是你最好的选择:https://dev59.com/vHE95IYBdhLWcg3wWMhQ - Reverend Gonzo
@Reverend Gonzo:谢谢,Perl的"use re 'debug'"接近我想要的东西。如果有类似的可以从Java中调用的东西就太好了。 - TimK
4个回答

9
如果匹配失败,Match.hitEnd()会告诉你是否有更长的字符串可以匹配。此外,您可以指定在输入序列中搜索匹配项的区域。因此,如果您有一个无法匹配的字符串,您可以测试其前缀以查看匹配失败的位置:
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class LastMatch {
    private static int indexOfLastMatch(Pattern pattern, String input) {
        Matcher matcher = pattern.matcher(input);
        for (int i = input.length(); i > 0; --i) {
            Matcher region = matcher.region(0, i);
            if (region.matches() || region.hitEnd()) {
                return i;
            }
        }

        return 0;
    }

    public static void main(String[] args) {
        Pattern pattern = Pattern.compile("[A-Z]+[0-9]+[a-z]+");
        String[] samples = {
                "*ABC",
                "A1b*",
                "AB12uv",
                "AB12uv*",
                "ABCDabc",
                "ABC123X"
        };

        for (String sample : samples) {
            int lastMatch = indexOfLastMatch(pattern, sample);
            System.out.println(sample + ": last match at " + lastMatch);
        }
    }
}

这个类的输出结果是:
*ABC: last match at 0
A1b*: last match at 3
AB12uv: last match at 6
AB12uv*: last match at 6
ABCDabc: last match at 4
ABC123X: last match at 6

这很好,尽管我觉得第二种情况有点令人困惑。整个字符串匹配成功,为什么要报告 4 呢?我建议使用以下代码: region.matches(); if (region.hitEnd()) ...。然后对于该情况返回 6。 - TimK
很好的发现。我只测试了部分匹配,没有考虑到完整字符串或其任何前缀的完全匹配。现在已经修复了这个问题。 - Andreas Mayer

3
你可以取得这个字符串,迭代它,并在每次迭代中从末尾移除一个或多个字符,然后检查是否达到了hitEnd()
int farthestPoint(Pattern pattern, String input) {
    for (int i = input.length() - 1; i > 0; i--) {
        Matcher matcher = pattern.matcher(input.substring(0, i));
        if (!matcher.matches() && matcher.hitEnd()) {
            return i;
        }
    }
    return 0;
}

intput.length() - ytg

1
你可以使用一对 replaceAll() 调用来指示输入字符串的正负匹配。比如说,你想验证一个十六进制字符串;下面的代码将指示输入字符串中的有效和无效字符。
String regex = "[0-9A-F]"
String input = "J900ZZAAFZ99X"
Pattern p = Pattern.compile(regex)
Matcher m = p.matcher(input)
String mask = m.replaceAll('+').replaceAll('[^+]', '-')
System.out.println(input)
System.out.println(mask)

这将打印以下内容,有效字符下面有一个+,无效字符下面有一个-
J900ZZAAFZ99X
-+++--+++-++-

0

如果你想在代码之外进行操作,我会使用 rubular 来测试正则表达式,然后再将其粘贴到代码中。


1
除非我漏掉了什么,否则这告诉我文本是否与正则表达式不匹配,但它并没有告诉我在哪里匹配失败。那就是我要找的。 - TimK

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接