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

时间:2017-05-11 12:08:03

标签: java java.util.scanner delimiter

鉴于以下代码,它输出:

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");
    }
}

3 个答案:

答案 0 :(得分:4)

让我们说X是分隔符。

如果我们扫描"aXbXc"这样的文字,很明显有3个令牌:"a" "b""c"

如果我们扫描"aXXc"这样的文字,仍有3个令牌,但这次:"a" """c"。这是因为我们将分隔符设置为一次只匹配一个X,因此它不会将另一个X视为已匹配分隔符的延续,而是另一个分隔符。
(这在分隔符为,的情况下非常有用,我们会扫描1,2,,,3之类的数据,因为代表元素:1 2 { {1}} noData noData)。
如果您希望分隔符代表一个或多个3,则需要使用X,因为X+是表示“一次或多次”的量词。这种方式+仅代表aXXc"a"元素,因为整个"c"将被视为一个分隔符。

其他有趣的案例是XX。如您所见,此处没有aXbX 文字以分隔符结尾 。在这种情况下,Scanner不会假设在最后一个分隔符后面有空元素,因此它只看到c"a"作为标记,而不是"b"

同样适用于"a", "b", "" 文字以分隔符开头 。扫描仪不会假设它之前有一些空元素。

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

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

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

答案 1 :(得分:3)

我读了一些Scanner documentation,其中包括:

  

根据分隔模式的类型,可能会返回空标记。例如,模式&#34; \ s +&#34;将返回没有空标记,因为它匹配分隔符的多个实例。划界模式&#34; \ s&#34;可以返回空标记,因为它一次只能传递一个空格。

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

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

答案 2 :(得分:1)

您尝试的两个空白模式都没有与默认分隔符匹配,即"\\p{javaWhitespace}+"documentation并没有明确说明:它只是说&#34;扫描程序使用分隔符模式将其输入分解为标记,默认情况下该分隔符模式与空格相匹配。&#34;俗语&#34;空白&#34;意味着任意数量的连续空白字符。

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