java.util.Scanner 的 useDelimiter( " " ) 或 useDelimiter(Pattern.compile( "\s" )) 的工作方式与标准行为不同



给定下面的代码,它输出:

Feed a chunk of data here:           
I have found:   0 words; 0 ints; 0 booleans;

如果我键入 10 个空格并保留两个 useDelimiter 方法调用注释和输出:

Feed a chunk of data here:           
I have found:   9 words; 0 ints; 0 booleans;
sssssssss

如果我键入完全相同的 10 个空格,但确实使用两个 useDelimiter 调用之一。 为什么会这样?应该不一样吧?这是代码,谢谢:

package com.riccardofinazzi.regex;
import java.io.Console;
import java.util.Scanner;
import java.util.regex.Pattern;
import java.util.ArrayList;
class ScanNext {
public static void main(String[] args) {
/* match counters */
int hits_s = 0, hits_i = 0, hits_b = 0;
/* current token value */
String  s;
Integer i;
Boolean b;
ArrayList<Object> al = new ArrayList<Object>();
Scanner s1 = new Scanner(System.console().readLine("Feed a chunk of data here: "));
/* not needed as this is def behaviour, I put it here to not forget the method */
//s1.useDelimiter(Pattern.compile("\s"));
//s1.useDelimiter(" ");
while(s1.hasNext()) {
if (        s1.hasNextInt()) {
al.add(s1.nextInt());       hits_i++;
} else if ( s1.hasNextBoolean()) {
al.add(s1.nextBoolean());   hits_b++;
} else {    al.add(s1.next());          hits_s++;
}
}
System.out.println("I have found:t"+hits_s+" words; "+hits_i+" ints; "+hits_b+" booleans;");
for (Object in : al) {
if (in instanceof String)
System.out.print("s");
if (in instanceof Integer)
System.out.print("i");
if (in instanceof Boolean)
System.out.print("b");
}
System.out.print("n");
}
}

假设X是分隔符。

如果我们像"aXbXc"一样扫描文本,很明显有 3 个标记:"a""b""c".

如果我们像"aXXc"一样扫描文本,仍然有 3 个标记,但这次:"a""""c".这是因为我们将分隔符设置为一次只匹配一个X,因此它不会将另一个X视为已匹配的分隔符的延续,而是单独的分隔符。
(这在,分隔符的情况下非常有用,我们像1,2,,,3一样扫描数据,因为它应该表示元素:12noDatanoData3)。
如果您希望分隔符表示一个或多个X则需要使用X+因为+是表示"一次或多次"的量词。这样aXXc将仅表示"a""c"元素,因为整个XX将被视为一个分隔符。

另一个有趣的案例是aXbX。如您所见,此处没有c文本以分隔符结尾。在这种情况下,Scanner 不会假定最后一个分隔符之后有空元素,因此它只看到"a""b"作为令牌,而不是"a", "b", ""

这同样适用于文本以分隔符开头XbXc。扫描仪不会假定它前面有一些空元素。


现在让我们回到你的案例。

如果您打印扫描仪的默认分隔符(使用类似System.out.println(s1.delimiter());的代码),您将看到它是p{javaWhitespace}+。因此,默认情况下分隔符是一个或多个空格。但稍后将其更改为单个空格或空格族。这意味着对于字符串

"          "
  • 如果p{javaWhitespace}+分隔符,则整个表达式将匹配为一个分隔符,因此分隔符之前、之后和之间没有元素,因此有 0 个标记(非分隔符元素)
  • 但是如果我们使用" ""\s"作为分隔符,那么 Scanner 将找到 10 个分隔符(每个空格是其中之一)。由于有 10 个分隔符,这意味着它们之间有 9 个元素(甚至空字符串计数)。此外,文本以分隔符开头和结尾,这意味着在第一个分隔符之前或最后一个分隔符之后没有标记。

我阅读了一些扫描仪文档,其中除其他外:

根据分隔模式的类型,可能会返回空令牌。例如,模式"\s+"将不返回空标记,因为它与分隔符的多个实例匹配。分隔模式"\s"可以返回空标记,因为它一次只传递一个空格。

观察到的行为的原因是默认分隔符,正如您在Scanner.WHITESPACE_PATTERN(来自 OpenJDK 的代码)和Scanner.reset()(将分隔符重置为该模式)中看到的那样,这是\p{javaWhitespace}+的。由于+,它将您的整个输入匹配为一个分隔符。

如果通过在末尾添加+来更改自定义分隔符,它们也会将连续空格视为一个分隔符。

您尝试的两个空格模式都与默认分隔符("\p{javaWhitespace}+".文档没有明确说明这一点:它只是说"扫描程序使用分隔符模式将其输入分解为标记,默认情况下与空格匹配。通俗地说,"空格"意味着任意数量的连续空格字符。

默认分隔符的最终规范仅在 Scanner.reset() 的文档中说明,该文档将分隔符重置为默认值。

最新更新