使用正则表达式解析Java Scanner换行符(Bug?)

时间:2010-05-20 00:51:47

标签: java regex newline java.util.scanner

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

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

仅供参考:我无法在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 个答案:

答案 0 :(得分:6)

您的useDelimiter()next()组合有问题。 useDelimiter("")将返回next()上的1长子字符串,因为空字符串实际上位于每两个字符之间。

也就是说,因为"\r\n".equals("\r" + "" + "\n")所以"\r\n"实际上是两个令牌,"\r""\n",由""分隔。

要获得Matcher - 行为,您需要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 [...]如果{{1} }是0,然后[...]此方法继续搜索输入,查找指定的模式而不受限制。

相关问题

答案 1 :(得分:3)

事实上,这是两者的预期行为。扫描仪主要关心使用分隔符将事物拆分为令牌。所以它(懒惰地)接受你的sourceString并将其视为以下一组标记:\r\n\n\r\r,{{ 1}}和\n。然后,当您调用hasNext时,它会检查下一个令牌是否与您的模式匹配(由于\n上的?,这些令牌都非常简单。因此while循环遍历7个标记中的每一个。

另一方面,匹配器会贪婪地匹配正则表达式 - 因此它会按预期将\r\n?捆绑在一起。

强调扫描程序行为的一种方法是将正则表达式更改为\r\n。这导致计数为0.这是因为扫描程序将第一个标记读取为(\\r\\n|\\n) \r),然后通知它与您的模式不匹配,所以当你致电\r\n时会返回false。

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

答案 2 :(得分:2)

值得一提的是,你的例子含糊不清。它可能是:

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

(七行)

或:

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

(五行)

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

答案 3 :(得分:0)

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