目录
① next() 方法默认的token分隔符(delimiter)为 “空格、回车和tab”
② 如果输入的第一个字符就是分隔符(空格、回车和tab),则会“忽略”:
④ 可以使用Scanner.useDelimiter()指定特定的String作为分隔符
利用while(hasNext())处理多个测试样例时,注意全局变量的初始化
1)Scanner类位置
Scanner是java.util包下的final类,不在java.lang包下:
要使用时须主动import(特别在刷题时)。
2)Scanner.next()使用:
方法说明:
大意:
-
从当前Scanner发现和返回下一个完整的token
-
完整的token前面和后面都是由特定pattern的分隔符(delimiter)隔开的
-
即使之前调用的hasNext()方法已经返回了true值,next()方法仍有可能在扫描输入时被阻塞
结合例子和方法说明,即next()方法会一个一个读入token(来自于屏幕,文件等等),不同token之间由分隔符(delimiter)隔开。
需要注意的是:
① next() 方法默认的token分隔符(delimiter)为 “空格、回车和tab”
和C/C++的 Scanf()一个个读入变量并赋值的分隔符是一样的,e.g:
回车做分隔符:
Tab做分隔符:
(空格作分隔符就是第一个例子,不需举例)
② 如果输入的第一个字符就是分隔符(空格、回车和tab),则会“忽略”:
关于token —— A complete token is preceded and followed by input that matches the delimiter pattern.即完整的token前面和后面都是由特定pattern的分隔符(delimiter)隔开的,根据输入的第一个字符是不是分隔符,可以分为两种情况:
- 第一个不是分隔符,就是正常字符,则默认为前面有个“虚拟的分隔符”,next()会读入第一个字符到第一个分隔符之间的所有内容作为a complete token,虽然此token“前面“并没有分隔符;
- 第一个是分隔符,则next()正常工作。
③ 方法第三点的说明:阻塞问题(待补充)
方法说明的第三点:即使之前调用的hasNext()方法已经返回了true值,next()方法仍有可能在扫描输入时被阻塞
可以看下面的例子:
④ 可以使用Scanner.useDelimiter()指定特定的String作为分隔符
e.g
⑤ nextXXX()方法(待补充)
除了可以使用useDelimiter()来指定特定的delimiter外,也可以使用nextXXX()来筛选不同已经得到的token,达到“获取特定分隔符下的特定字符”的目的,nextXXX()有一下几种(基于jdk1.8):
可以看到,除了八大基本类型外,还有提供了BigDecimal,BigInteger的筛选,其中整数类型提供了附加的带参next方法:nextByte(int),nextShort(int),nextInt(int i),nextLong(int),nextBigInteger(int),这个参数是:
这个是指定进制的,如:
并且有一个nextLine()方法,这个方法将在后面详细介绍。
3)Scanner.hasNext()
hasNext()方法,hasNextXXX()方法与next(),nextXXX()方法是对应的:
翻看hasNext()方法说明:
-
如果本scanner(指正在运行的这个实例即this)的输入中有此token(基于默认或者设置的delimiter),则返回true,否则false,并且此方法可能在等到扫描输入时被阻塞。
其余的hasNextXXX()方法同理。
但是有一点要注意:
利用while(hasNext())处理多个测试样例时,注意全局变量的初始化
用
处理就好,牛客网上给的范例一般都是这样。
但是有一点特别注意,这样使用的全局变量在while开头一定要进行初始化,不然上一个用例的结果必然会影响这一次的答案。(吃过亏!!!)
4)nextLine()
- 将此scanner推进到当前行之后(past the current) ,返回中间跳过的输入——— 即换行后,并返回上一次光标所在位置到上一行末尾的内容。(之所以不用“返回上一行”,因为上一次扫描的位置可能不在行头)
- 此方法返回当前行的剩余部分,不包括任何行末的行分隔符。新的扫描位置设置为下一行的开头
- 因为此方法会持续在输入中查找行分隔符,如果没有行分隔符出现的话,它可能会缓冲所有的输入并跳过(以寻找行分隔符)。
很明显了,nextLine()是截取当前光标到当前行末尾(不包括行分隔符)的内容,并将光标移到下一行开头。
实验:
类似于之前的next()和hasNext()方法的对比,nextLine()也有对应的hasNextLine()方法与之对应
nextXXX()nextLine()合用的问题
应该特别引起注意的是,Scanner.next()方法和Scanner.nextLine()方法合用时的问题,先看下面的例子:
或者:
按说
这一句接收了“5”,接下来:
不应该接收了456256吗?为什么接收了一个空的输入,然后才接收456256呢。可以回看next()方法(nextInt()和所有nextXXX()的实现都是next()方法,只是对得到Token进行了再判断而已)和nextLine()方法说明:
next()方法:
-
从当前Scanner发现和返回下一个完整的token
-
完整的token前面和后面都是由特定pattern的分隔符(delimiter)隔开的
即next()方法是将输入用delimeter(空格,回车和Tab)切割成一个一个Token,返回第一个。
而nextLine()方法:
- 将此scanner推进到当前行之后(past the current) ,返回中间跳过的输入——— 即换行后,并返回上一次光标所
位置到上一行末尾的内容。(之所以不用“返回上一行”,因为上一次扫描的位置可能不在行头)
可以看到,nextLine()是返回当前光标位置到行末的内容,然后将光标换行到下一行开头,问题就出在这,再看上例的输入:
从输出结果来看(只能这么分析了),检测到5后面的回车键(\n)后,将Token返回,此时光标没有移到下一行(就是读到了'\n'而已),而是在回车符前面,类似于这样|\n,当执行scan.nextLine()时,从当前光标处读到下一行(其实它的实现是从当前光标处读到下一个换行符处(回车键),skip掉这一段),当然就是一个空的字符串了——|和\n之间上面都没有。
为了验证,看下面代码:
int i = scan.nextInt()之后,光标位于\n之前,使用scan.useDelimiter(" ")使scan,next()能够接收回车键,然后使用next()将回车接收放入after5中,打印出来验证——果然,换行了(真的换行了。。。。开始以为怎么看到回车效果??)。Dubug一样能看到:
分析完原因之后,现在来看怎么解决这个问题。
一方面,应该尽量避免nextXXX后使用nextLine()(明显,反之是可以的),可以全部使用nextLine()处理,这样能保证肯定都是按行处理,光标永远都是在每一行的头部。代价就是明明很简单的事情(如 int i = scan.nextInt())只能通过nextLine()接收,再从字符串中(nextLine()返回值是String)提取。
另一方面,对症下药,既然nextInt()后光标在行尾的\n前面,那么我们可以单独使用一个nextLine()“吃掉”这个回车,像这样: