Java刷题常用API(一)———— Scanner从屏幕读入数据(待补充)

目录

1)Scanner类位置

2)Scanner.next()使用:

① next() 方法默认的token分隔符(delimiter)为 “空格、回车和tab”

② 如果输入的第一个字符就是分隔符(空格、回车和tab),则会“忽略”:

③ 方法第三点的说明:阻塞问题(待补充)

④ 可以使用Scanner.useDelimiter()指定特定的String作为分隔符

⑤ nextXXX()方法(待补充)

3)Scanner.hasNext()

利用while(hasNext())处理多个测试样例时,注意全局变量的初始化

4)nextLine()

nextXXX()nextLine()合用的问题


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()“吃掉”这个回车,像这样:

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值