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 上找到。