【问题标题】:System.in.read() behaviour i can't explainSystem.in.read() 行为我无法解释
【发布时间】:2017-04-06 12:12:21
【问题描述】:
class E92StringDemo {

    public static void main(String args[]) throws java.io.IOException {

        String strObj1 = "First String";

        for (int i = 0; i < strObj1.length(); i++) {

            System.out.print(strObj1.charAt(i));
            System.in.read(); //just to pause the execution till i press enter key

        }
    }
}

我希望输出如下:

F
i
r
s
t...

但输出如下:

F
ir
st
S
tr
in
g

我不知道为什么每次按下enter 键(\n)都会在一行中显示 2 个字符?

我正在运行 windows 8 并使用命令提示符使用javac 运行文件。

【问题讨论】:

  • 您在什么操作系统上运行?如果您在 Windows 上运行,则 enter 是回车换行,即“\r\n” - 两个字符。
  • @JonSkeet 它在 window 上工作正常,刚刚测试过
  • @JonSkeet 我在 Linux Mint 上进行了检查,它工作得很好。在按下enter 之后,它会一个字母一个字母地进行。
  • @user3260381 这是因为当你按下回车键时,就像 Jon 在 Windows 中已经指出的那样,你正在生成两个字符:回车符 \r 和换行符 \n 允许 System.in.read() 执行两次。
  • @XtremeBaumer 是什么让您认为它被跳过了?在循环开始时,我们打印第一个字符,然后等待用户输入,即回车键,因此控制台将光标移动到下一行,但在标准输入中我们有两个字符,所以现在System.in.read() 将读取两次,允许两次迭代(单行中的两个字符)。当不再有字符时,进程等待用户输入,当用户再次按 Enter 键时,我们将移至下一行,标准输入再次有 \r\n 可以读取。

标签: java string output


【解决方案1】:

问题

System.in.read() 仅在标准输入流(由System.in 表示)中没有要读取的数据时才保留应用程序的执行。

但是在控制台中,当你按下 ENTER 时,会发生两件事:

因此,如果您想在每次下一次迭代中暂停循环,则需要在离开当前迭代之前清空输入流中的数据。但是System.in.read() 一次只读取 一个 字符,在您的情况下,\r 留下\n 进行下一次迭代(所以不要在那里暂停)。

因此,在暂停再次可用之前,您需要在一次迭代中阅读两次。

解决方案

如果您想以独立于操作系统的方式解决此问题,请使用 BufferedReader#readLine()Scanner#nextLine 之类的:

String strObj1 = "First String";
try(Scanner sc = new Scanner(System.in)){//will automatically close resource
    for (int i = 0; i < strObj1.length(); i++) {
        System.out.print(strObj1.charAt(i));
        sc.nextLine();
    }
}

这些方法还解决了在按 Enter 之前放置的潜在额外字符的问题,因为它们中的每一个也将被放置在标准输入流中,这将需要额外的 .read() 调用。


* 以及在按 Enter 之前提供的其余潜在字符

【讨论】:

    【解决方案2】:

    Windows 上的 ENTER 生成 2 个字符 (CRLF) 而 read() 只消耗其中 1 个。 您必须消耗 2 个字符才能获得所需的行为。只需添加另一个System.in.read(),您就会看到。

    下面解释了按下 ENTER 时字符的生成和消耗。 13代表CR,10代表LF。 F 13i10r 13s10t 13 10S 13t10r 13i10n 13g10

    【讨论】:

    • 您的回答从根本上解释了我在代码中遗漏的内容,清除了我的查询周围的概念并巧妙地使其工作。
    • @user3260381 虽然添加另一个 System.in.read() 在您的特定情况下可能是有效的解决方案,但值得一提的是,它仅适用于 Windows,因为其他操作系统使用单行分隔符。另一个潜在问题是System.in.read() 将等到用户按下回车键,但它不会阻止用户在按下回车键之前输入其他字符,这也将进入标准输入(您尝试从中读取字符的System.in)更多超过两个字符,这可能会导致额外的循环。为了解决这个问题,最好消耗整行,而不仅仅是行分隔符。
    【解决方案3】:

    这将解决您遇到的问题,但我无法解释为什么您使用 System.in.read() 会出现这种奇怪的行为。

    class E92StringDemo {
        public static Scanner reader = new Scanner(System.in);
    
        public static void main(String[] args) {
    
            String strObj1 = "First String";
    
            for(int i = 0; i < strObj1.length(); i++) {
                System.out.print(strObj1.charAt(i));
                reader.nextLine(); //just to pause the execution till i press enter key
            }
        }
    }
    

    【讨论】:

      【解决方案4】:

      使用

        new Scanner(System.in).nextLine();
      

      而不是

       System.in.read();
      

      您还可以使用System.in.read 获得此结果,因为它除了返回一个字节的所有可能值之外,还返回一个int,它还需要能够返回一个额外的值来指示流结束。因此,它必须返回一个可以表达比字节更多的值的类型。

      但是根据其文档。

      /**
       * Reads the next byte of data from the input stream. The value byte is
       * returned as an <code>int</code> in the range <code>0</code> to
       * <code>255</code>. If no byte is available because the end of the stream
       * has been reached, the value <code>-1</code> is returned. This method
       * blocks until input data is available, the end of the stream is detected,
       * or an exception is thrown.
       *
       * <p> A subclass must provide an implementation of this method.
       *
       * @return     the next byte of data, or <code>-1</code> if the end of the
       *             stream is reached.
       * @exception  IOException  if an I/O error occurs.
       */
      
      public abstract int read() throws IOException;
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-12-22
        • 1970-01-01
        • 1970-01-01
        • 2015-07-08
        • 2021-10-31
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多