Java输入输出方法总结
Scanner库
在做牛客OJ输入输出练习场时的笔记,JDK版本为1.8.0_251,idea为2020.1.2,所写很多是个人理解,敬请指正。下文的代码均在IDEA上运行调试,输入的每行都会有”\n”。并且下面函数,只要scanner对象没有读到值,为空,那么当前函数都会等待输入,不会执行下一句代码。
基本用法
创建Scanner的一个实例,并将System.in传入Scanner类构造器中,这时就算在控制台输入信息,scan对象也不会读取任何东西(通过调试猜想)。
可以看到上图中变量框内buf为””,代表当前scan读入的值为空。
1 | public static void main(String[] args) { |
Scanner读入数据可以使用next()和nextLine()函数。
next()函数用法
next()函数会等待当前的输入,并且读入当前的有效字符
- 未读到有效字符时,会一直等待读入有效字符,这时,空格和回车是无效字符;
- 当读到有效字符时,空格会成为无效字符,空格后的字符不会读入,而回车作为终止符,代表这一行输入结束,next()函数也结束,例如用例2和用例3;
- 未读到终止符时,一直等待输入。
1 | public static void main(String[] args) { |
1 | 注:"[in]:"后的字符为输入,"[out]:"后的字符为输出。 |
nextLine()函数用法
nextLine()函数和next函数基本功能一致,但是nextLine()会将空格作为有效字符读入,回车作为终止符。
1 | public static void main(String[] args) { |
1 | 用例1 |
hasNext()
hasNext()会等待当前输入,和next()函数类似。
- 但是当hasNext()读到有效字符时,并且输入回车终止符后,hasNext()会返回true;
- 在未读到有效字符时,和next()一样,空格和回车都是无效字符。
1 | public static void main(String[] args) { |
可以看到下面的代码,while会一直运行,hasNext()一直等待下一个输入,当有输入有效字符并回车后,hasNext()返回true,循环体内scan.next()读到输入的字符。当输入一个数据并回车后(例如3),scan.next()会读取3,随后hasNext()读到\n,这时\n时无效字符,所以hasNext()等待有效字符的输入,停在判断语句处。
1 | import java.util.Scanner; |
hasNextLine()
hasNextLine()会等待当前输入,具体机制是通过正则表达式匹配当前位置及之后的字符。
- 当匹配到时,例如当前位置为\n,或者当前位置为有效字符,但有效字符后由结束符(\n或\r或\n\r或\r\n),hasNextLine()会返回true;
- 当匹配不到时,会返回false。
- 当匹配到有效字符,但是没有结束符,会一直等待输入结束符
1 | private static final String LINE_SEPARATOR_PATTERN = |
hasNextInt()
这个函数和hasNext()类似,多了判断读到的值是不是int的逻辑,如果当前读到的是空格或者回车,函数不会停止,仍然等待输入有效字符,直到输入数字或非数字,当输入非数字时,hasNextInt返回false,循环结束。
1 | public class test { |
关于牛客网和IDEA的实验结果不同的原因
以牛客OJ在线编程常见输入输出练习场中的A题目为例:
1 | 题目描述:计算a+b |
hasNext() IDEA A题
采用复制粘贴形式,将牛课上用例粘贴到IDEA控制台,用例如下
1 | import java.util.Scanner; |
可以看到直接粘贴是没有最后的\n,如果自己一行一行敲进去会有\n。
运行到第二次int b时,会等待输入结束符回车,输入回车也就是\n后,buf变为”1 5\n10 20\n”,并且开始继续运行第三次循环你,而这时的hasNext()会一直等待输入,卡在第三次循环判断处。
hasNext() 牛客 A题
采用hasNext(),代码和上面IDEA实验的一样
- 如下图,采用hasNext(),调试时可以看到scan读到的是”1 5 10 20 “(没有”\n”,在IDEA上自己输入会有”\n”,但是20后面有一个空字符,不清楚是什么),并且position为0,代表当前要读的字符位置为0,即第一个字符。
- 如下图,当第一次读完a和b时,已经将1和5读入了scan.nextInt(),这时下一个要读的字符的位置为3,即第四个字符(空格算一个字符)
- 如下图,当第二次执行到while的判断语句时,position为3,这时hasNext()可以判定还有字符,返回true,于是进入循环体
- 如下图,当第二次读完a后,position变为6,是10后面那个空格的位置,即第七个字符,当然下一次读20时,空格会省去,这是next()的特点,可以看后文next()函数的详细介绍。
- 如下图,当第二次读完a和b后,position变为9,而9的位置刚好在20后面,这里猜想有两种可能:
1)猜想1:牛客网上用例输入系统后,没有回车\n这种符号(IDEA上面有”\n”),于是第三次循环时,hasNext()函数会返回false,也就不会进行第三次循环,而是执行到函数末尾了。那么position为9的数据是什么?如果是空格那hasNext()遇见空格会继续等待输入,也就不是空格;那如果是回车,就变成了猜想2
2)猜想2:牛客网上每一行结尾都有一个\n,但是调式系统没有显示,hasNext()碰到\n时认为没有接收到有效字符,接着往后面读,进而读到了后面一行的数字;当整个用例文件被读完的时候,按照hasNext()函数的特点,会一直等待继续输入,但是因为文件被读完,可能发出了文件结束的信号,从而程序运行结束。
因此更相信猜想2,于是得出以下结论:
结论:hasNext()运行一次,在未读到有效字符时,空格和回车都是无效字符;在读到了有效字符后,读到\n就执行完了当前这句代码,没有读到\n的话,会一直等待输入,知道输入终止符\n,hasNext()才会停止。牛客网上用例中,可能每一行结尾都有一个\n,而牛客网上的next和nextLine类函数可以停止等待输入,可能是文件读取结束发出信号所致(猜想)。
1 | 这个代码框内为最开始的想法,现在看来有误,只是保留在此 |
hasNextLine() IDEA A题
现在,在IDEA上测试下hasNextLine(),代码和用例如下
1 | import java.util.Scanner; |
用例直接粘贴到控制台内,可以看到buf中为”1 5\n10 20”
当第一次读完a和b时,position变为3,指向第一个\n
当第二次运行完while的判断语句后,可以看到position仍未3,但是hasNextPattren匹配到了后面还有信息
当执行到第二次b的读入时,程序卡住了,等待输入,这时输入回车,就可以继续读入b的值
敲入回车后,看到position为9,buf也变为”1 5\n10 20\n”,这时9指向”\n”
在继续执行后,hasNextLine()仍然判断到后面有信息,进入循环体内。
这时执行到a的读入,nextInt()开始等待输入,程序变得无法结束
hasNextLine() 牛客 A题
现在,再使用hasNextLine()调试一下,代码如下
1 | import java.util.Scanner; |
- 如下图,当即将执行到while的判断语句hasNextLine()时,scan并没有读取到任何东西,当执行hasNextLine()后,才会读取输入,并判断是否有一行数据
- 如下图,当执行完hasNextLine()后,进入循环体,还未执行a这行代码。可以看到buf是”1 5 10 20 “,hasNextLine()以此判断有数据,返回true,才进入了循环体。
- 如下图,当即将执行到while的判断语句hasNextLine()时,position为9,已经指向空了,但是hasNextLine()在这里会返回true,判断机制还没有搞清楚,因此当执行到循环体第一句时,就会报错Exception occurred: java.util.NoSuchElementException (uncaught)Signal Dispatcher:
由此猜想牛客对用例的输入可能采取了某些方法,当然在只有牛客用例的时候,无法进行控制台输入,也就和上面IDEA的结果不同,IDEA是阻塞等待输入,而牛客这里直接报错。
当我们采用nextLine(),代码如下,读取这一行,相当于跳过当前行,这样下一次while判断时,hasNextLine()就不会返回true了,成功解决A题目。
但是仅仅是输入行中没有”\n”时才可以用nextLine()来辅助hasNextLine(),当有”\n”时hasNextLine()永远返回true,无法跳出循环。
1 | import java.util.Scanner; |
- 看一下nextLine改变了什么,当执行nextLine之前,position为3,就是数字5后面的字符
- 当执行nextLine后,position为4,猜想当hasNextLine()遇见position为3的”\n”时,会返回true,而遇见position为4的”1”时,继续判断这一句,发现后面有”\n”,匹配成功,返回true。(假设牛客上的用例,每句末尾都有\n)
- 但是,当运行到第二次循环时,nextLine()将position变为10,见下面两张图,hasNextLine()在第三次判断时,没有\n字符了,甚至什么也没有,也就返回false,程序结束。
hasNextLine() IDEA G题
再看一个hasNextLine()的实验,在IDEA上实验题目G,输入用例1,发现结尾没有\n,hasNextLine()会等待输入,知道等到\n,当输入\n后,这个\n前面的这行0都被读入,但是接下来hasNextLine()仍然会等待\n,从而陷入无限循环;当输入用例2时,IDEA会正常通过三行hasNextLine(),到达第四次循环并等待输入。
1 | import java.util.Scanner; |
1 | 用例1: |
hasNextLine() 牛客 G题
代码正常运行,因为有一句nextLine(),因此程序正常结束
1 | import java.util.Scanner; |
综上,在使用Scanner进行输入输出时,尽量避免使用hasNextLine()。以下参考在查阅资料时给了很大启发,附在下面。(但是有些函数在牛客可以结束,但是在IDEA就无线等待输入还是没搞清楚)
https://www.nowcoder.com/discuss/8050
https://blog.csdn.net/weixin_36242811/article/details/105463062