【问题标题】:Is there a way to sometimes require data from stdin and sometimes not in Java?有没有办法有时需要来自标准输入的数据,有时不需要来自 Java 的数据?
【发布时间】:2012-04-25 16:25:41
【问题描述】:

我正在使用下面的代码在命令行从标准输入读取一条 sql 语句:

    BufferedReader in = null;
    StringBuilder sb = new StringBuilder();
    String line = null;
    try {
        in = new BufferedReader(new InputStreamReader(System.in));
        while((line = in.readLine()) != null) {
            sb.append(line);
        }
    } finally {
        if (in!=null) in.close();
    }

我的问题是应用程序有时需要使用来自标准输入的数据运行,有时则不需要(无管道输入)。如果上述代码中没有输入,in.readLine() 会阻塞。有没有办法重写这段代码,以便在没有任何输入的情况下仍然可以运行?

更新:该应用程序旨在从命令行而不是键盘接收管道数据。

【问题讨论】:

  • Stdin 代表控制台。您如何知道用户是否要输入内容?
  • 谢谢彼得 - 更新了问题以澄清。
  • 在这种情况下 in.readLine() 只会在等待来自文件系统的数据时阻塞。这应该很短(大约 10 毫秒)我你的文件系统很慢,这似乎大部分时间都在阻塞,尽管实际上它正在处理一些数据并反复等待磁盘。

标签: java pipe stdin


【解决方案1】:

我认为没有任何方法可以检查您最终是否会获得另一行输入。请注意,如果用户关闭输入流(例如,在终端中使用 ^D),您当前的代码会终止。

BufferedReader.ready() 检查流中是否有一些数据。像 T.J.提到过,您可能不走运并在实际收到数据之前就要求提供数据,您的用户会因为您没有回答他们的查询而感到难过。

Scanner.hasNextLine() 是一个阻塞操作,所以可能不是您想要的。

您可以让用户指定是否从 System.in 读取,例如使用命令行参数。

【讨论】:

  • 我最终使用了完美运行的 BufferedReader.ready()。谢谢!
  • 稍微解释一下——我发现这只有在BufferedReader 可以立即使用输入时才能正常工作。例如,当从查询数据库的命令行工具输入数据时,ready() 将立即返回 false,因为往返数据库需要几毫秒。相反,我必须按照您的建议编写命令行参数。
【解决方案2】:

您可以使用ready 来测试流是否已准备好数据。如果ready 返回false,当你调用它时,流没有准备好数据(它可能在一微秒后接收到数据)。如果你调用ready 并且它返回true,你知道你可以在没有阻塞的情况下调用read。 (当然,您可能无法在不阻塞的情况下拨打readLine。)

【讨论】:

    【解决方案3】:

    支持选择性地从标准输入读取的 Unix 实用程序通常会根据其命令行确定要执行的操作。例如,如果命令行上没有命名文件,cat 将从标准输入读取。你可以按照这些思路做一些事情——添加一个命令行标志或选项或其他东西来指示程序是否应该尝试从标准输入读取任何内容。

    如果没有您希望程序读取的输入,另一种方法是从 /dev/null 重定向输入。在这种情况下,您可以从标准输入读取并立即获得文件结束指示。 windows相当于是从NUL重定向。

    如果你真的需要你的程序来检测它是否可以无阻塞地从标准输入读取,请查看InputStream.available()。但是,如果您想支持某人在程序的输入中键入(或将其复制粘贴到终端窗口中),那么在用户实际键入内容之前,InputStream 不会将输入显示为可用。

    【讨论】:

    • 另外,许多 unix utils 支持“特殊”输入文件 '-',意思是“stdin”。
    【解决方案4】:

    在 unix 上,您可以检查 /proc/self/fd/0 是否指向文件、管道或来自控制台。

    $ ls -l /proc/self/fd/0
    lrwx------ 1 peter peter 64 Apr 25 18:12 /proc/self/fd/0 -> /dev/pts/21
    $ ls -l /proc/self/fd/0 < /dev/null
    lr-x------ 1 peter peter 64 Apr 25 18:13 /proc/self/fd/0 -> /dev/null
    $ echo Hello World | ls -l /proc/self/fd/0 
    lr-x------ 1 peter peter 64 Apr 25 18:13 /proc/self/fd/0 -> pipe:[139250355]
    

    这将告诉您输入是文件、已通过管道传输还是终端。

    【讨论】:

      【解决方案5】:

      这取决于没有管道输入时程序的运行方式。一般来说,一个程序会有一个开放的标准输入,你所能做的就是设置一个计时器,等待一段时间的输入,如果计时器在没有输入的情况下到期,假设不会有任何输入。另一种方法是安排程序在标准输入关闭的情况下运行,在这种情况下,当您尝试阅读时会得到一个 EOF。

      要在 unix(包括 Mac OS)中关闭标准输入,您需要编写一个 bash 脚本来启动您的程序:

      #!/bin/bash
      
      exec 0>&- # close stdin
      
      java -jar yourProgram.jar # run your program
      

      当然,如果您还是从 bash 命令行启动,则不需要脚本:

      prompt> java -jar yourProgram.jar 0>&-
      

      但如果你的程序在 Java EE 容器中运行,我不知道你会如何在启动前关闭标准输入,也许你不能。

      【讨论】:

        猜你喜欢
        • 2019-09-05
        • 1970-01-01
        • 2014-02-07
        • 2021-08-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多