1. 概述

Scanner 类提供了一套方法,简化了通过分割成多个标记来解析输入的过程。它通常用于从控制台和文件等不同来源读取输入数据。

在这个简短的教程中,我们将重点讲解 next()nextLine() 方法之间的差异。

尽管这两个方法乍看相似,但它们在功能上大相径庭。

2. next() 方法

通常情况下,Scanner 使用分隔符模式将输入划分为标记,其默认模式是任何空格字符。

因此,next() 方法如其名所示,只读取输入中的下一个标记,直到遇到分隔符为止

2.1. 使用默认分隔符

正如前面提到的,Scanner 类默认使用空格作为分隔符。

例如,如果我们输入 "Hello world",next() 将只会读取到 "Hello"。剩下的单词 "world" 将留给下一次调用 next()

下面是一个使用 next() 的示例测试:

@Test
void givenInput_whenUsingNextMethod_thenReturnToken() {
    String input = "Hello world";
    try (Scanner scanner = new Scanner(input)) {
        assertEquals("Hello", scanner.next());
        assertEquals("world", scanner.next());
    }
}

在这里,Scanner 使用空格字符解析输入。第一次调用 next() 读取 "Hello",第二次调用则读取 "world"。

多个空格也被视为一个空格,结果相同:

@Test
void givenInput_whenUsingNextMethodWithMultipleWhiteSpaces_thenReturnToken() {
    String input = "Hello        world";
    try (Scanner scanner = new Scanner(input)) {
        assertEquals("Hello", scanner.next());
        assertEquals("world", scanner.next());
    }
}

包括制表符和换行在内的多个空格也是如此:

@Test
void givenInput_whenUsingNextMethodWithTabAndNewLine_thenReturnToken() {
    String input = "Hello    \t\n world";
    try (Scanner scanner = new Scanner(input)) {
        assertEquals("Hello", scanner.next());
        assertEquals("world", scanner.next());
    }
}

请注意,空格包括多种字符,如制表符 (\t)、换行符 (\n),以及更多字符(参见:Oracle Java 文档)。

2.2. 使用自定义分隔符

Scanner 类提供了通过 useDelimiter() 方法方便地重写默认分隔符的方法。

让我们看看当使用自定义分隔符时,next() 方法会如何工作。

例如,我们将使用冒号 : 作为分隔符:

@Test
void givenInput_whenUsingNextMethodWithCustomDelimiter_thenReturnToken() {
    String input = "Hello :world";
    try (Scanner scanner = new Scanner(input)) {
        scanner.useDelimiter(":");

        assertEquals("Hello ", scanner.next());
        assertEquals("world", scanner.next());
    }
}

如上所述,next() 会读取 "Hello",然后这次是空格。原因是 Scanner 使用 : 将输入划分为标记,而不是空格。

3. nextLine() 方法

另一方面,**nextLine() 方法会消耗整个输入行,包括空格字符,直到遇到换行符 \n。**

换句话说,我们可以使用此方法读取包含默认分隔符(如空格)的输入。它会在接收到 \n 或按下 Enter 键后停止读取。

让我们实际操作一下:

@Test
void givenInput_whenUsingNextLineMethod_thenReturnEntireLine() {
    String input = "Hello world\nWelcome to baeldung.com";
    try (Scanner scanner = new Scanner(input)) {
        assertEquals("Hello world", scanner.nextLine());
        assertEquals("Welcome to baeldung.com", scanner.nextLine());
    }
}

如图所示,第一次 scanner.nextLine() 读取了整个值 "Hello world",而第二次则消耗了剩余的输入。

next() 不同,nextLine() 在读取完输入后将光标移动到下一行。然而,next() 保持光标在同一行。

这里的一个重要点是,与 next() 不同,自定义分隔符不会改变 nextLine() 的行为

我们通过测试案例来确认这一点:

@Test
void givenInput_whenUsingNextLineWithCustomDelimiter_thenIgnoreDelimiter() {
    String input = "Hello:world\nWelcome:to baeldung.com";
    try (Scanner scanner = new Scanner(input)) {
        scanner.useDelimiter(":");

        assertEquals("Hello:world", scanner.nextLine());
        assertEquals("Welcome:to baeldung.com", scanner.nextLine());
    }
}

不出所料,nextLine() 方法忽略我们的自定义分隔符,继续读取输入,直到找到 \n 字符。

接下来,我们将展示其他分隔符也会被视为换行符,产生相同的结果:

回车符 (\r) 和回车换行符 (\r\n) 也被 nextLine() 视为换行符:

@Test
void givenInput_whenUsingNextLineMethodWithCR_thenReturnEntireLine() {
    String input = "Hello world\rWelcome to baeldung.com";
    try (Scanner scanner = new Scanner(input)) {
        assertEquals("Hello world", scanner.nextLine());
        assertEquals("Welcome to baeldung.com", scanner.nextLine());
    }
}

同样,对于回车换行符:**

@Test
void givenInput_whenUsingNextLineMethodWithCRLF_thenReturnEntireLine() {
    String input = "Hello world\r\nWelcome to baeldung.com";
    try (Scanner scanner = new Scanner(input)) {
        assertEquals("Hello world", scanner.nextLine());
        assertEquals("Welcome to baeldung.com", scanner.nextLine());
    }
}

4. 区别总结

简而言之,比较 next()nextLine() 方法时,要注意以下几点:

  • nextLine() 返回整个文本到换行符。next() 根据给定的分隔符(默认为空格)读取标记化的文本。
  • nextLine() 在读取输入后将扫描器位置移到下一行。而 next() 保持光标在同一行。

5. 别的方式分割字符串

如果你想使用 Scanner 类作为标记化机制,可以使用其他方法,如 String.split()。根据需求,你可以使用 split() 方法的不同变体:

  • split(String regex, int limit) - 根据提供的正则表达式分割字符串。第一个参数是正则表达式,第二个参数是该模式应用到提供的字符串上的次数。
  • split(String regex):根据提供的正则表达式分割字符串。模式将在字符串结束前无限次应用。

6. 结论

在这篇文章中,我们详细解释了 Scanner.next()Scanner.nextLine() 方法之间的区别。

如往常一样,所有示例的完整源代码可以在 GitHub 上找到。