4

我正在用 Java 手动开发语法分析器,我想使用正则表达式来解析各种令牌类型。问题是,如果输入不符合语法,我还希望能够准确地报告当前行号。

长话短说,当我尝试将换行符与 Scanner 类实际匹配时遇到了问题。具体来说,当我尝试使用 Scanner 类将换行符与模式匹配时,它会失败。几乎总是。但是,当我使用 Matcher 和相同的源字符串执行相同的匹配时,它也会完全按照您的预期检索换行符。这是否有原因,我似乎无法发现,或者这是我怀疑的错误?

仅供参考:我无法在 Sun 数据库中找到描述此问题的错误,因此如果是错误,则尚未报告。

示例代码:

Pattern newLinePattern = Pattern.compile("(\\r\\n?|\\n)", Pattern.MULTILINE);
String sourceString = "\r\n\n\r\r\n\n";
Scanner scan = new Scanner(sourceString);
scan.useDelimiter("");
int count = 0;
while (scan.hasNext(newLinePattern)) {
    scan.next(newLinePattern);
    count++;
}
System.out.println("found "+count+" newlines"); // finds 7 newlines
Matcher match = newLinePattern.matcher(sourceString);
count = 0;
while (match.find()) {
    count++;
}
System.out.println("found "+count+" newlines"); // finds 5 newlines
4

4 回答 4

6

你的useDelimiter()next()组合有问题。useDelimiter("")将返回 1 长度的子字符串next(),因为实际上每两个字符之间确实存在一个空字符串。

也就是说,因为"\r\n".equals("\r" + "" + "\n")so"\r\n"实际上是两个标记,"\r""\n",由 分隔""

要获得Matcher-behavior,您需要findWithinHorizon忽略分隔符。

    Pattern newLinePattern = Pattern.compile("(\\r\\n?|\\n)", Pattern.MULTILINE);
    String sourceString = "\r\n\n\r\r\n\n";
    Scanner scan = new Scanner(sourceString);
    int count = 0;
    while (scan.findWithinHorizon(newLinePattern, 0) != null) {
        count++;
    }
    System.out.println("found "+count+" newlines"); // finds 5 newlines

API 链接

  • findWithinHorizon(Pattern pattern, int horizon)

    尝试查找指定模式的下一次出现 [...] 忽略分隔符 [...] 如果未检测到此类模式,null则返回 [...] 如果horizon为 0,则 [...] 此方法继续无限制地搜索输入以查找指定的模式。

相关问题

于 2010-05-20T01:28:47.030 回答
3

也就是说,事实上,两者的预期行为。扫描器主要关心使用分隔符将事物拆分为标记。因此,它(懒惰地)获取您的 sourceString 并将其视为以下标记集:\r\n\n\r\r\n\n?然后,当您调用 hasNext 时,它会检查下一个标记是否与您的模式匹配(由于on the ,它们都可以轻松完成\r\n?)。因此,while 循环遍历 7 个标记中的每一个。

另一方面,匹配器会贪婪地匹配正则表达式——所以它会\r\n像你期望的那样将 s 捆绑在一起。

强调 Scanner 行为的一种方法是将您的正则表达式更改为(\\r\\n|\\n). 这导致计数为 0。这是因为扫描器将第一个标记读取为\r( not \r\n ),然后注意到它与您的模式不匹配,因此在您调用时返回 false hasNext

(简短版:扫描器在使用标记模式之前使用您的分隔符进行标记,匹配器不执行任何形式的标记)

于 2010-05-20T01:11:40.350 回答
2

值得一提的是,您的示例模棱两可。它可能是:

\r
\n
\n
\r
\r
\n
\n

(七行)

或者:

\r\n
\n
\r
\r\n
\n

(五行)

这 ?您使用的量词是一个贪婪的量词,这可能会使五个正确答案,但是因为 Scanner 迭代令牌(在您的情况下是单个字符,由于您选择的分隔模式),它会不情愿地匹配,一次一个字符,得出七的错误答案。

于 2010-05-20T01:09:50.673 回答
0

当您使用Scanner带有分隔符的 时"",将生成每个字符长的标记。这是在应用新行正则表达式之前。然后它将这些字符中的每一个与新行正则表达式匹配;每一个都匹配,所以它产生 7 个令牌。但是,因为它将字符串拆分为 1 个字符的标记,所以它不会将相邻的\r\n字符组合成一个标记。

于 2010-05-20T01:12:27.677 回答