给定下面的代码,它输出:
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
一样扫描数据,因为它应该表示元素:1
2
noData
noData
3
)。
如果您希望分隔符表示一个或多个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() 的文档中说明,该文档将分隔符重置为默认值。