Java 关于Scanner的hasNext()方法的二三事

在编程笔试(或者某些场景)中,可能存在这样的需求:程序被要求接收不确定数量的一些字符串或者是数字,然后对接收的数据进行相关的处理。

假设这样一个场景,程序被要求接收不定数量的一些整数,然后计算这些数字的和。来看下面这个程序:

import java.util.Scanner;public class Test {public static void main(String[] args) {Scanner s = new Scanner(System.in);int sum = 0;while(s.hasNextInt()) {sum += s.nextInt();}System.out.println(sum);}
}

我们的预期是:程序开始运行后,我们通过键盘在终端键入一行不定数量的整数,按下回车,程序紧接着打印出整数之和,然后结束运行。看上去似乎条例清晰,逻辑严密。不过程序实际运行的情况与我们的预期出现了一点偏差:在我们输入数据并按下回车之后,光标移动到了下一行,不过程序并没有打印出我们期待的结果,也没有结束运行,它“停下”了,或者说,它似乎在等待用户继续输入。如果此时你继续输入一行整数,然后按下回车,光标移动到下一行,情况不会发生变化。甚至你不输入任何内容,直接按下回车,情况仍然不会发生变化。但是如果你输入了一个浮点数或者是一个字符串,程序会紧接着打印出你想要的结果,然后结束运行。这很好理解,因为你输入的不是整数,循环会因此结束。

再来看这样一个场景,程序被要求接收不确定数量的一些字符串,然后将其逆序输出。来看下面这个程序:

import java.util.Scanner;public class Test {public static void main(String[] args) {Scanner s = new Scanner(System.in);String str = "";while(s.hasNext()) {str = s.next() + " " + str;}System.out.println(str);}
}

我们的预期是:程序开始运行后,我们通过键盘在终端键入一行不定数量的字符串,按下回车,程序紧接着逆序打印出字符串,然后结束运行。然而这个程序实际运行的情况更加糟糕,似乎不论我们输入任何类型、任何数量的内容,程序都不会从循环中跳出,就好像没有什么条件能够使s.hasNext()的结果为false

这种情况令人感到沮丧,看来我们必须调查一下到底哪里出现了问题。显而易见,问题的出现肯定和Scanner类的hasNext()方法有关,那就让我们首先来看一下 jdk 的开发者对这个方法的描述:

Returns true if this scanner has another token in its input.
This method may block while waiting for input to scan.
The scanner does not advance past any input.

重点看这一句:**This method may block while waiting for input to scan. **大意是此方法可能会在等待输入时阻塞。这样一解释就好办了,我们在循环体里面打印一下 hasNext() 方法的返回值看看:

public static void main(String[] args) {Scanner s = new Scanner(System.in);boolean b;while(b = s.hasNext()) {System.out.println(b);System.out.println("your input is " + s.next());}System.out.println(b);
}

程序执行的结果如下:

i am iron man // 这句是输入,下面的都是输出
true
your input is i
true
your input is am
true
your input is iron
true
your input is man
// 输出换行到这儿以后程序并未从循环中跳出

可以看到,程序将输入的最后一个单词man打印出以后,既没有跳出循环,打印出false;也没有进入下一次循环,打印出trues.hasNext()在这儿发生了阻塞,程序在等待用户输入,而用户不想再继续输入了。一方面,程序并不把空格、制表符以及回车换行这样的不可见字符当作输入,空白字符被当作输入内容的分隔符,回车换行则被用于提交输入;另一方面,似乎用户输入任何可见字符,s.hasNext()都会返回true。

真的,没有办法了吗?


其实是有解决办法的,而且不止一种,请看下面:


方案一(Windows系统适用):ctrl + Z

如果是在Windows系统的cmd窗口中运行此程序,按下ctrl+Z,窗口会出现^Z这样的符号,按下回车,程序打印出false,然后结束运行。请注意,程序是正常结束的,所以说:并非用户输入任何可见字符,s.hasNext()都会返回true,将^Z作为输入,hasNext()会返回false这个方案在cmd窗口中有效,而在IDEA自带的控制台中无效,不过在IDEA中按下ctrl+D可以达到同样的效果。

另外,有StackOverflow用户指出,对于Linux/Unix/MacOS环境,ctrl+D可以达到同样的效果,不过我没有做过相关的测试,仅仅在这里提一下。


方案二:使用hasNext(String pattern)

可以使用hasNext()的重载方法hasNext(String pattern)通过特殊终止符结束循环,就像下面这样:

public static void main(String[] args) {Scanner s = new Scanner(System.in);while(!s.hasNext("exit")) {System.out.println("your input is " + s.next());}
}

当输入exit,循环结束。有点类似于在mysql命令行程序中输入quit,或者是在cmd中输入exit


方案三:使用break跳出循环

在循环体中通过特殊终止符触发break,例如下面这样:

public static void main(String[] args) {Scanner s = new Scanner(System.in);while(s.hasNext()) {String tmp = s.next();if(tmp.equals("quit")) break;System.out.println("your input is " + tmp);}
}

当输入quit,循环终止。


方案四:重定向输入流

前面说过,hasNext()方法可能会在等待输入时阻塞,可能的意思就是说:存在某种情况,hasNext()不会发生阻塞。比如说下面介绍的这种方案:

既然问题是由键盘输入带来的,不如就把产生问题的源头干掉,我们可以重定向标准输入使得系统从文件而不是从键盘读取输入。操作很简单,提前在一个文本文件中编辑好输入内容,然后在cmd中执行程序,只需给执行命令增加一点内容,像下面这样:

java Test < text.txt //text.txt中包含了待输入的内容。

按下回车,程序从text.txt中读取内容,告别从键盘获取输入。

这个方案有一个优点是用户不再需要手动从键盘输入数据,尤其是当我们做测试的时候,这种方式能帮我们节省很多时间和精力,输入量越大,这种优势越为明显。IDEA中可以配置输入重定向,打开 Run Configurations 就可以找到,这里我不详细描述了。

另外,一些朋友提到:在编程网站中提交代码while(s.hasNext())可以自己跳出循环,而在本地 IDEA 中使用while(s.hasNext())没有办法退出循环,其实就是因为在线编程网站将输入流重定向了。


总结四种方案:

方案号 办法 备注
方案一 ctrl+Z Windows系统适用
方案二 使用hasNext(String pattern)
方案三 使用break跳出循环 最朴素的思路
方案四 重定向输入流 节省时间和精力

参考资料:

[1] Robert Sedgewick and Kevin Wayne 著,谢路云 译.算法.北京:人民邮电出版社,2012:24.

[2] trutheality.StackOverflow.https://stackoverflow.com/questions/10490344/how-to-get-out-of-while-loop-in-java-with-scanner-method-hasnext-as-condition

[3] while(hasNextInt())为什么不会结束循环.百度知道.https://zhidao.baidu.com/question/135843130068219805.html

[4] java的Scanner类的hasNext()方法问题.百度知道.https://zhidao.baidu.com/question/505075549.html

[5] 如何在JAVA中使用SCANNER方法“HASNEXT”作为条件退出WHILE循环?.dovov编程网.http://www.dovov.com/javascannerhasnextwhile.html

Java Scanner的hasNext()方法相关推荐

  1. java scanner的hasnext,Java Scanner hasNext()方法

    Java Scanner hasNext()方法 java.util.Scanner.hasNext() 方法如果此scanner有另一个标记在它的输入,则返回true.在等待要scanner的输入, ...

  2. Java Scanner类的方法及用法(很详细)

    一.Scanner类简介 Java 5添加了java.util.Scanner类,这是一个用于扫描输入文本的新的实用程序.它是以前的StringTokenizer和Matcher类之间的某种结合.由于 ...

  3. java hasnext_Java Page.hasNext方法代码示例

    import org.springframework.data.domain.Page; //导入方法依赖的package包/类 @GetMapping public ResponseEntity&g ...

  4. Java Scanner类的常用方法及用法(很详细)

    Java Scanner类的方法及用法(很详细) Scanner类简介 Java 5添加了java.util.Scanner类,这是一个用于扫描输入文本的新的实用程序.它是以前的StringToken ...

  5. java中hasnext的作用_java中Scanner的hasNext()的疑问

    第一个问题,两段代码的区别在于阻塞的位置不同,加上一行输出代码就可以很明显地看到差别. Test.java import java.util.Scanner; public class Test { ...

  6. java scanner输入数组_Java Scanner输入两个数组的方法

    题目 从命令行读入两个数组的长度和数组的值,其中第一行两个数na和nb代表aa和bb数组的长度 代码 import java.util.Scanner; public class Z { public ...

  7. java中scanner中nextint,Java Scanner nextInt()方法

    Java Scanner nextInt()方法 java.util.Scanner.nextInt() 方法扫描输入的下一个标记为int.形式nextInt()方法的调用和调用nextInt(rad ...

  8. Java Scanner next()方法与示例

    扫描仪类的next()方法 (Scanner Class next() method) Syntax: 句法: public String next(); public String next(Pat ...

  9. Java Scanner nextLine()方法与示例

    扫描仪类nextLine()方法 (Scanner Class nextLine() method) nextLine() method is available in java.util packa ...

最新文章

  1. sql server存储过程中SELECT 与 SET 对变量赋值的区别
  2. django中序列化器字段参数、关联字段
  3. 使用ThreadLocal来解决问题
  4. php 特殊符号处理,处理特殊符号的方法-php技巧
  5. 【操作系统笔记】中断和异常
  6. python小孩的报酬_孩子怎么区分报酬与奖励
  7. 2022最新LOGO在线制作系统源码
  8. ArcGIS 制作3D遥感影像图
  9. 利用天翎知识文档+群晖NAS搭建企业知识库,享用智能检索
  10. md5加密数据表中的密码php,JSP_使用MD5加密数据库中的用户密码(一),我们知道,现在网络上一般的 - phpStudy...
  11. word邮件合并发送记录_如何将Word文档作为电子邮件正文发送
  12. apk在android12设备无法安装(兼容android 12或更高版本)
  13. Android Q中通知的设置
  14. DetectoRS论文解读
  15. 搜索引擎优化,SEO关键词快速排名方法有哪些?
  16. js本地预览图片和转base64
  17. 【CCF-CSP】202112-3 登机牌条码
  18. windows store下载_小巧又精致!这款 App 让你的 Windows 任务栏更好看
  19. Altium 学习笔记
  20. 机器学习中qa测试_机器学习项目测试怎么做?(看实例)

热门文章

  1. 对excel表格按照某个字段拆分
  2. mount point / 挂载点
  3. openCV+Python实战练习——银行卡号识别
  4. 六、总线(一)总线概述
  5. SpringBoot实现邮箱发送验证码
  6. Python函数复习
  7. 【MacOS】Mac下的gVim - MacVim
  8. 云服务器密码忘了_云服务器 root 密码忘记了怎么办
  9. 附录002《 Git 中的重要概念》
  10. 利用百度(或者360搜索等)的快照解决公司网络限制