[Java] [缓冲区] 不要在同一个输入流使用多个Scanner

代码(Java)

预期代码可满足下面的功能:输入三个字符串,每输入一个字符串后,立即打印它。

代码0(从文件输入)

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.Scanner;


public final class Main {

  public static void main(String[] args) throws FileNotFoundException {

      InputStream f = new FileInputStream("Input.txt");

      Scanner in_0 = new Scanner(f);
      String s0 = in_0.next();
      System.out.println("Your 0 String is " + s0);

      Scanner in_1 = new Scanner(f);
      String s1 = in_1.next();
      System.out.println("Your 1 String is " + s1);

      Scanner in_2 = new Scanner(f);
      String s2 = in_2.next();
      System.out.println("Your 2 String is " + s2);

      System.out.println("END.");
  }
}

Input.txt
Input.txt

Output:
Your 0 String is A
Exception in thread “main” java.util.NoSuchElementException
at java.base/java.util.Scanner.throwFor(Scanner.java:937)
at java.base/java.util.Scanner.next(Scanner.java:1478)
at Main.main(Main.java:18)
注:第18行为 String s1 = in_1.next();

代码1(与代码1相同,只不过改为从命令行输入)

import java.io.FileNotFoundException;
import java.util.Scanner;


public final class Main {

  public static void main(String[] args) throws FileNotFoundException {

      Scanner in_0 = new Scanner(System.in);
      String s0 = in_0.next();
      System.out.println("Your 0 String is " + s0);

      Scanner in_1 = new Scanner(System.in);
      String s1 = in_1.next();
      System.out.println("Your 1 String is " + s1);

      Scanner in_2 = new Scanner(System.in);
      String s2 = in_2.next();
      System.out.println("Your 2 String is " + s2);

      System.out.println("END.");
  }
}

Input & Output
InputAndOutput

倘若改为每输入一个字母就按一次Enter
InputAndOutput1

观察

可见,只有当从命令行输入,且每次只输入一个字符串时,程序才运行正常。

若非如此,
从命令行输入的程序都将会在in_0之后,一直处于等待输入的状态;
从文件输入的程序将会出现NoSuchElementException,认为A之后就没有字符串而到达EOF了,即使我们的文件中确确实实有5(当然大于2)个字符串。

解释

上面出现的问题可归因于:我们在程序中使用了多个Scanner

Scanner如何作用

Scanner内部利用正则表达式,将缓冲区的输入内容进行分析,帮助我们快速得到需要的输入数据。下面是这一过程的实现步骤:

  1. 我们在命令行,或通过文件,输入内容;
  2. 内容进入标准输入缓冲区
  3. Scanner标准输入缓冲区的内容拿到自己的缓冲区内,并进行分析;
  4. Scanner根据我们的需求,从自己的缓冲区再次拿走相应内容,返回给我们;
  5. 我们得到相应内容。

造成上述问题的关键是3、4步。下面,我们利用这个5步模版解释具体的代码。

对于代码0(从文件输入)

[注意6中的“等也没有用”]

  1. 我们通过文件输入内容A B C D E;
  2. 内容A B C D E进入标准输入缓冲区
  3. in_0标准输入缓冲区的内容拿到自己的缓冲区内,并进行分析;此后,标准输入缓冲区内容为in_0缓冲区内容为A B C D E。当然,in_1 in_2缓冲区也为
  4. in_0根据我们的需求in_0.next(),获取字符串,从自己的缓冲区再次拿走相应内容A,返回给我们;此后,标准输入缓冲区内容为in_0缓冲区内容变为B C D E。当然,in_1 in_2缓冲区也为
  5. 我们得到相应内容,屏幕上打印Your 0 String is A
  6. in_1想要重复上面的in_0的操作,但是标准输入缓冲区已被in_0读完了,A以外的内容现在在in_0自己的缓冲区内。in_1看到的只是空空如也的标准输入缓冲区等也没有用,因为文件内容已经全部到了标准输入缓冲区(当然后来又被in_0取走了),文件已经到底(EOF),不可能再有内容;
  7. 就这样,无辜的in_1只能说,NoSuchElementException

对于代码1(从命令行输入,一次性输入)

道理与上面类似。

对于代码1(从命令行输入,每输入完,都按Enter

[注意6中的等待是有用的]

  1. 我们在命令行输入内容A
  2. 内容进入标准输入缓冲区
  3. in_0标准输入缓冲区的内容拿到自己的缓冲区内,并进行分析;此后,标准输入缓冲区内容为in_0缓冲区内容为A。当然,in_1 in_2缓冲区也为
  4. in_0根据我们的需求,从自己的缓冲区再次拿走相应内容,返回给我们;此后,标准输入缓冲区内容为in_0缓冲区内容变为。当然,in_1 in_2缓冲区也为
  5. 我们得到相应内容,屏幕上打印Your 0 String is A
  6. in_1想要重复上面的in_0的操作,但是标准输入缓冲区已被in_0读完了。不过,由于这是命令行,所以我们随时可以输入内容,不存在上面的EOF,输入没有到底。所以,in_1开始等待我们输入新的内容;
  7. 我们在命令行输入内容B
  8. 内容进入标准输入缓冲区
  9. in_1标准输入缓冲区的内容拿到自己的缓冲区内,并进行分析;此后,标准输入缓冲区内容为in_1缓冲区内容为B。当然,in_0 in_2缓冲区也为
  10. 我们得到相应内容,屏幕上打印Your 1 String is B
  11. 如此进行下去……

总结

导致上面问题的关键点是:

  1. 输入内容会全部进入输入缓冲区Scanner会把输入缓冲区的内容全部拿入自己的缓冲区,然后才开始分析我们的需求;
  2. 不同Scanner的缓冲区是不同的。

总地来说,就是不要使用多个Scanner。同样的道理扩展开来,就是Do not create multiple buffered wrappers on a single byte or character stream

更正

import java.io.FileNotFoundException;
import java.util.Scanner;


public final class Main {

  public static void main(String[] args) throws FileNotFoundException {

      Scanner in = new Scanner(System.in);

      String s0 = in.next();
      System.out.println("Your 0 String is " + s0);

      String s1 = in.next();
      System.out.println("Your 1 String is " + s1);

      String s2 = in.next();
      System.out.println("Your 2 String is " + s2);

      System.out.println("END.");

  }
}

Correct

参考

  1. Do not create multiple buffered wrappers on a single byte or character stream
  2. StackOverFlow-How to use multiple Scanner objects on System.in?
  • 8
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值