0%

Java输入输出方法总结

Java输入输出方法总结

Scanner库

在做牛客OJ输入输出练习场时的笔记,JDK版本为1.8.0_251,idea为2020.1.2,所写很多是个人理解,敬请指正。下文的代码均在IDEA上运行调试,输入的每行都会有”\n”。并且下面函数,只要scanner对象没有读到值,为空,那么当前函数都会等待输入,不会执行下一句代码。

基本用法

创建Scanner的一个实例,并将System.in传入Scanner类构造器中,这时就算在控制台输入信息,scan对象也不会读取任何东西(通过调试猜想)。

20210316160146

可以看到上图中变量框内buf为””,代表当前scan读入的值为空。

1
2
3
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
}

Scanner读入数据可以使用next()和nextLine()函数。

next()函数用法

next()函数会等待当前的输入,并且读入当前的有效字符

  1. 未读到有效字符时,会一直等待读入有效字符,这时,空格和回车是无效字符;
  2. 当读到有效字符时,空格会成为无效字符,空格后的字符不会读入,而回车作为终止符,代表这一行输入结束,next()函数也结束,例如用例2和用例3;
  3. 未读到终止符时,一直等待输入。
1
2
3
4
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
System.out.print("输入的数据为:" + scan.next());
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
注:"[in]:"后的字符为输入,"[out]:"后的字符为输出。
用例1
[in]:hello world
[out]:输入的数据为:hello
---
用例2
[in]: hello world
[out]:输入的数据为:hello
---
用例3
[in]:

hello world
[out]:输入的数据为:hello

nextLine()函数用法

nextLine()函数和next函数基本功能一致,但是nextLine()会将空格作为有效字符读入,回车作为终止符。

1
2
3
4
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
System.out.println("输入的数据为:" + scan.nextLine());
}
1
2
3
4
5
6
7
8
9
10
11
12
用例1
[in]: hello world
[out]: hello world

用例2(这里输入和输出中three space前后都有三个空格,空格作为有效字符)
[in]: three space
[out]: three space

用例3(这里输入为回车,输出也为空)
[in]:

[out]:

hasNext()

hasNext()会等待当前输入,和next()函数类似。

  1. 但是当hasNext()读到有效字符时,并且输入回车终止符后,hasNext()会返回true;
  2. 在未读到有效字符时,和next()一样,空格和回车都是无效字符。
1
2
3
4
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
System.out.println("输入的数据为:" + scan.hasNext());
}

可以看到下面的代码,while会一直运行,hasNext()一直等待下一个输入,当有输入有效字符并回车后,hasNext()返回true,循环体内scan.next()读到输入的字符。当输入一个数据并回车后(例如3),scan.next()会读取3,随后hasNext()读到\n,这时\n时无效字符,所以hasNext()等待有效字符的输入,停在判断语句处。

1
2
3
4
5
6
7
8
9
10
11
12
import java.util.Scanner;

public class test {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);

// 判断是否还有输入
while(scan.hasNext()) {
System.out.println("输入的数据为:" + scan.next());
}
}
}

hasNextLine()

hasNextLine()会等待当前输入,具体机制是通过正则表达式匹配当前位置及之后的字符。

  1. 当匹配到时,例如当前位置为\n,或者当前位置为有效字符,但有效字符后由结束符(\n或\r或\n\r或\r\n),hasNextLine()会返回true;
  2. 当匹配不到时,会返回false。
  3. 当匹配到有效字符,但是没有结束符,会一直等待输入结束符
1
2
3
private static final String LINE_SEPARATOR_PATTERN =
"\r\n|[\n\r\u2028\u2029\u0085]";
private static final String LINE_PATTERN = ".*("+LINE_SEPARATOR_PATTERN+")|.+$";

hasNextInt()

这个函数和hasNext()类似,多了判断读到的值是不是int的逻辑,如果当前读到的是空格或者回车,函数不会停止,仍然等待输入有效字符,直到输入数字或非数字,当输入非数字时,hasNextInt返回false,循环结束。

1
2
3
4
5
6
7
8
9
10
11
public class test {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);

// 判断是否还有输入
while(scan.hasNextInt()) {
int number = scan.nextInt();
System.out.println("输入的数据为:" + number);
}
}
}

关于牛客网和IDEA的实验结果不同的原因

以牛客OJ在线编程常见输入输出练习场中的A题目为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
题目描述:计算a+b

输入描述:输入包括两个正整数a,b(1 <= a, b <= 10^9),输入数据包括多组。

输出描述: 输出a+b的结果

示例:
输入:
1 5
10 20
输出:
6
30

hasNext() IDEA A题

采用复制粘贴形式,将牛课上用例粘贴到IDEA控制台,用例如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import java.util.Scanner;

public class Main{
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
while (scan.hasNext()) {
int a = scan.nextInt();
int b = scan.nextInt();
System.out.println(a+b);
}
}
}

用例:
1 5
10 20
scan.buf:"1 5\n10 20"

可以看到直接粘贴是没有最后的\n,如果自己一行一行敲进去会有\n。

运行到第二次int b时,会等待输入结束符回车,输入回车也就是\n后,buf变为”1 5\n10 20\n”,并且开始继续运行第三次循环你,而这时的hasNext()会一直等待输入,卡在第三次循环判断处。

hasNext() 牛客 A题

采用hasNext(),代码和上面IDEA实验的一样

20210317153915

  1. 如下图,采用hasNext(),调试时可以看到scan读到的是”1 5 10 20 “(没有”\n”,在IDEA上自己输入会有”\n”,但是20后面有一个空字符,不清楚是什么),并且position为0,代表当前要读的字符位置为0,即第一个字符。

20210317154043

  1. 如下图,当第一次读完a和b时,已经将1和5读入了scan.nextInt(),这时下一个要读的字符的位置为3,即第四个字符(空格算一个字符)

20210317154101

  1. 如下图,当第二次执行到while的判断语句时,position为3,这时hasNext()可以判定还有字符,返回true,于是进入循环体

20210317154321

  1. 如下图,当第二次读完a后,position变为6,是10后面那个空格的位置,即第七个字符,当然下一次读20时,空格会省去,这是next()的特点,可以看后文next()函数的详细介绍。

20210317154350

  1. 如下图,当第二次读完a和b后,position变为9,而9的位置刚好在20后面,这里猜想有两种可能:

1)猜想1:牛客网上用例输入系统后,没有回车\n这种符号(IDEA上面有”\n”),于是第三次循环时,hasNext()函数会返回false,也就不会进行第三次循环,而是执行到函数末尾了。那么position为9的数据是什么?如果是空格那hasNext()遇见空格会继续等待输入,也就不是空格;那如果是回车,就变成了猜想2

2)猜想2:牛客网上每一行结尾都有一个\n,但是调式系统没有显示,hasNext()碰到\n时认为没有接收到有效字符,接着往后面读,进而读到了后面一行的数字;当整个用例文件被读完的时候,按照hasNext()函数的特点,会一直等待继续输入,但是因为文件被读完,可能发出了文件结束的信号,从而程序运行结束。

20210317154513

因此更相信猜想2,于是得出以下结论:

结论:hasNext()运行一次,在未读到有效字符时,空格和回车都是无效字符;在读到了有效字符后,读到\n就执行完了当前这句代码,没有读到\n的话,会一直等待输入,知道输入终止符\n,hasNext()才会停止。牛客网上用例中,可能每一行结尾都有一个\n,而牛客网上的next和nextLine类函数可以停止等待输入,可能是文件读取结束发出信号所致(猜想)。

1
2
3
4
5
6
7
8
9
10
11
这个代码框内为最开始的想法,现在看来有误,只是保留在此

这个写在最前面,防止掉入陷阱。牛客网的判题系统的所有测试用例都保存在文件中,而文件中每一行末尾没有"\n",自己在IDEA的控制台输入时,会读到"\n",这就导致了在循环或者判断时,hasNext()和hasNextLine()在不同平台上结果会有差异。(可以自己在牛客和IDEA调试一下,看看scan对象的buf变量就知道了)

hasNext()对于没有\n的一行,判false

hasNextLine()对于没有\n的一行,判true (假定牛课上每一行结尾没有\n,那么无论有没有\n都会判true)(如果牛客上每一行结尾都有\n的话,那么hasNextLine()是判true,从而进循环体报错,没有\n会等待输入结束符。)

hasNext()对于有\n的一行,判true,无限循环

hasNextLine()对于有\n的一行,判true,无限循环

hasNextLine() IDEA A题

现在,在IDEA上测试下hasNextLine(),代码和用例如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import java.util.Scanner;

public class A {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);

while (scan.hasNextLine()) {
int a = scan.nextInt();
int b = scan.nextInt();
System.out.println(a+b);
}
}
}

用例:
1 5
10 20
scan.buf:"1 5\n10 20"

用例直接粘贴到控制台内,可以看到buf中为”1 5\n10 20”

20210317161319

当第一次读完a和b时,position变为3,指向第一个\n

20210317161915

当第二次运行完while的判断语句后,可以看到position仍未3,但是hasNextPattren匹配到了后面还有信息

20210317161945

当执行到第二次b的读入时,程序卡住了,等待输入,这时输入回车,就可以继续读入b的值

20210317162349

敲入回车后,看到position为9,buf也变为”1 5\n10 20\n”,这时9指向”\n”

20210317162540

在继续执行后,hasNextLine()仍然判断到后面有信息,进入循环体内。

20210317162730

这时执行到a的读入,nextInt()开始等待输入,程序变得无法结束

20210317162841

hasNextLine() 牛客 A题

现在,再使用hasNextLine()调试一下,代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
import java.util.Scanner;

public class Main{
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);

while (scan.hasNextLine()) {
int a = scan.nextInt();
int b = scan.nextInt();
System.out.println(a+b);
}
}
}
  1. 如下图,当即将执行到while的判断语句hasNextLine()时,scan并没有读取到任何东西,当执行hasNextLine()后,才会读取输入,并判断是否有一行数据

20210316200122

  1. 如下图,当执行完hasNextLine()后,进入循环体,还未执行a这行代码。可以看到buf是”1 5 10 20 “,hasNextLine()以此判断有数据,返回true,才进入了循环体。

20210316200302

  1. 如下图,当即将执行到while的判断语句hasNextLine()时,position为9,已经指向空了,但是hasNextLine()在这里会返回true,判断机制还没有搞清楚,因此当执行到循环体第一句时,就会报错Exception occurred: java.util.NoSuchElementException (uncaught)Signal Dispatcher:

20210316210955

  1. 由此猜想牛客对用例的输入可能采取了某些方法,当然在只有牛客用例的时候,无法进行控制台输入,也就和上面IDEA的结果不同,IDEA是阻塞等待输入,而牛客这里直接报错。

  2. 当我们采用nextLine(),代码如下,读取这一行,相当于跳过当前行,这样下一次while判断时,hasNextLine()就不会返回true了,成功解决A题目。但是仅仅是输入行中没有”\n”时才可以用nextLine()来辅助hasNextLine(),当有”\n”时hasNextLine()永远返回true,无法跳出循环。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import java.util.Scanner;

public class Main{
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);

while (scan.hasNextLine()) {
int a = scan.nextInt();
int b = scan.nextInt();
System.out.println(a+b);
scan.nextLine();
}
}
}
  1. 看一下nextLine改变了什么,当执行nextLine之前,position为3,就是数字5后面的字符

20210317163748

  1. 当执行nextLine后,position为4,猜想当hasNextLine()遇见position为3的”\n”时,会返回true,而遇见position为4的”1”时,继续判断这一句,发现后面有”\n”,匹配成功,返回true。(假设牛客上的用例,每句末尾都有\n)

20210317163918

  1. 但是,当运行到第二次循环时,nextLine()将position变为10,见下面两张图,hasNextLine()在第三次判断时,没有\n字符了,甚至什么也没有,也就返回false,程序结束。
    20210317164208

20210317164221

hasNextLine() IDEA G题

再看一个hasNextLine()的实验,在IDEA上实验题目G,输入用例1,发现结尾没有\n,hasNextLine()会等待输入,知道等到\n,当输入\n后,这个\n前面的这行0都被读入,但是接下来hasNextLine()仍然会等待\n,从而陷入无限循环;当输入用例2时,IDEA会正常通过三行hasNextLine(),到达第四次循环并等待输入。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import java.util.Scanner;

public class G {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);

while (scan.hasNextLine()) { //当前代码可以在牛客上通过
int result = 0;
String s = scan.nextLine();
Scanner rowIn = new Scanner(s);
while (rowIn.hasNextInt()) {
result += rowIn.nextInt();
}
System.out.println(result);
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
用例1:
1 2 3
4 5
0 0 0 0 0
用例1在IDEA中变量显示:"1 2 3\n4 5\n0 0 0 0 0"
用例1在牛客中变量显示:"1 2 34 50 0 0 0 0"
----
用例2:
1 2 3
4 5
0 0 0 0 0

用例2在IDEA中变量显示:"1 2 3\n4 5\n0 0 0 0 0\n"

hasNextLine() 牛客 G题

代码正常运行,因为有一句nextLine(),因此程序正常结束

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import java.util.Scanner;

public class G {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);

while (scan.hasNextLine()) { //当前代码可以在牛客上通过
int result = 0;
String s = scan.nextLine();
Scanner rowIn = new Scanner(s);
while (rowIn.hasNextInt()) {
result += rowIn.nextInt();
}
System.out.println(result);
}
}
}

综上,在使用Scanner进行输入输出时,尽量避免使用hasNextLine()。以下参考在查阅资料时给了很大启发,附在下面。(但是有些函数在牛客可以结束,但是在IDEA就无线等待输入还是没搞清楚)

https://www.nowcoder.com/discuss/8050
https://blog.csdn.net/weixin_36242811/article/details/105463062