首页 > 解决方案 > 相对于 java.util.Scanner 中评估的正则表达式是什么?

问题描述

我注意到以下我无法解释的奇怪行为:

import java.util.Scanner;

public class Main {
  public static void main(String[] args) {
    var scanner = new Scanner("ab");

    System.out.println(scanner.findInLine("."));  // output: a
    System.out.println(scanner.findInLine("."));  // output: b
  }
}

import java.util.Scanner;

public class Main {
  public static void main(String[] args) {
    var scanner = new Scanner("ab");

    System.out.println(scanner.findInLine("."));  // output: a
    System.out.println(scanner.findInLine("^.")); // output: null
  }
}

我不明白第二个片段与第一个片段有何不同。

根据文档^匹配一行的开头,但情况似乎并非如此,因为:

  1. a在行匹配的开头,
  2. a在输入匹配的开头,并且
  3. b在扫描仪匹配的位置。

如果正则表达式没有相对于扫描仪的位置进行评估,那么它是相对于什么进行评估的?

标签: javaregexjava.util.scanner

解决方案


这一切都在那里,只是记录不佳。下面是如何findInLine实现的:

public String findInLine(Pattern pattern) {
    // omitted: validate parameters
    // omitted: ensure the internal buffer is large enough

    return findWithinHorizon(pattern, horizonForLine);
}

来源:java/util/Scanner.java

以下是文档必须说的内容findWithinHorizon

[...] 扫描仪将地平线视为透明的、非锚定边界 [...]。

来源:java.util.Scanner#findWithinHorizo​​n(java.util.regex.Pattern,int)

以及关于锚定范围:

[...] 如果没有锚定边界,则此匹配器区域的边界将无法匹配锚点,例如^$。[...]

来源:java.util.regex.Matcher##useAnchoringBounds(boolean)

下面是如何findWithinHorizon实现的:

public String findWithinHorizon(Pattern pattern, int horizon) {
    // omitted: validate parameters

    while (true) {
        if (findPatternInBuffer(pattern, horizon)) {
            matchValid = true;
            return matcher.group();
        }

        // omitted: check if more input is required
    }
    return null;
}

来源:java/util/Scanner.java

下面是如何findPatternInBuffer实现的:

private boolean findPatternInBuffer(Pattern pattern, int horizon) {
    // omitted: calculating search limit

    matcher.region(position, searchLimit);

    // omitted: matching pattern
}

java/util/Scanner.java

这就是为什么第二个模式不匹配的原因。正则表达式只在光标前面搜索,但不考虑^适用于光标位置。然而,它适用于输入的开头,因此匹配的第一个模式。

这并不意味着每个正则表达式^都会失败,后面的积极看法会起作用:

import java.util.Scanner;

public class Main {
  public static void main(String[] args) {
    var scanner = new Scanner("ab");

    System.out.println(scanner.findInLine("."));        // output: a
    System.out.println(scanner.findInLine("(?<=^.).")); // output: b
  }
}

推荐阅读