命令 shell 上的 java 进程之间的管道工作不可靠

发布于 2024-11-03 13:37:32 字数 778 浏览 4 评论 0原文

我正在尝试在两个 java 程序之间传输文本。为了简单起见,我展示了这段代码:

import java.io.DataInputStream;
import java.io.IOException;

public class Test {
    public static void main(String[] args) throws IOException {
        DataInputStream stdin = new DataInputStream(System.in);
        String completeText = ""; 

        while (stdin.available() > 0) {
            byte[] tempByte = { stdin.readByte() };
            completeText += new String(tempByte);
        }

        System.out.println(completeText);
    }
}

在 Linux 或 Windows 上执行以下操作时,文本似乎被省略,就好像管道阻塞或丢失相当随机一样。有时一切都会通过,有时则不然:

echo "omg wtf" | java Test | java Test

对此有什么想法吗? CPU 速度越慢,文本通过的频率似乎越高。当输入从 java System.out.println() 传送时,“available”是否会因任何原因返回错误的结果?

干杯!

I'm trying to pipe text between two java programs. For the sake of simplicity I'm presenting this code:

import java.io.DataInputStream;
import java.io.IOException;

public class Test {
    public static void main(String[] args) throws IOException {
        DataInputStream stdin = new DataInputStream(System.in);
        String completeText = ""; 

        while (stdin.available() > 0) {
            byte[] tempByte = { stdin.readByte() };
            completeText += new String(tempByte);
        }

        System.out.println(completeText);
    }
}

When doing the following on linux or windows, the text seems to get omitted as if the pipe was blocking or lost quite random. Sometimes everything gets through, sometimes not:

echo "omg wtf" | java Test | java Test

Any ideas on this? The slower the cpu the more often the text gets through, it seems. Is "available" returning the wrong result for any reason when the input is piped from java System.out.println()?

Cheers!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

鹿童谣 2024-11-10 13:37:32

首先,available() 方法不是确定流是否耗尽的可靠方法。流结束的可靠指示是通过检查 read() 方法的返回值(<0 表示流结束)。

简而言之,如果流暂时为空,则 available() 可以返回 false (这将终止循环)。如果管道仍然处于活动状态,一旦管道另一端的进程向其中写入一些字节,这种情况就会改变。为了确保读取所有数据,您需要检查流结束。

其次,如果您想读取字符(并将它们连接成字符串),您应该从 Reader (而不是来自流的字节)。这将允许您的代码处理 unicode 字符。

第三,如果使用 StringBuilder(而不是普通字符串)。

最后,如果您只需要读取字节,则可以直接使用输入流(不需要用 DataInputStream 包装它)。

我是这样写的:

Reader r = new InputStreamReader(System.in);
StringBuilder sb = new StringBuilder();
while(true) {
  int ch = r.read();
  if(ch < 0)
    break;
  sb.append((char) ch);
}

System.out.println(sb.toString());

First, The available() method is not a reliable way to determine whether a stream is exhausted. A reliable indication to an end-of-stream is by checking the return value of the read() method (< 0 means end-of-stream).

In short, available() can return false (which will terminate the loop), if the stream is momentarily empty. If the pipe is still active, this situation will change as soon as the process on the other end of the pipe writes some bytes into it. To be sure that all data was read you need to check for end-of-stream.

Second, if you want to read characters (and concatenate them into a string) you should read characters from a Reader (and not bytes from a stream). This will allow your code to handle unicode characters.

Third, concatenation of large chunks of characters is will be faster if you use a StringBuilder (rather than a plain String).

Finally, if you only need to read bytes, you can use the input-stream directly (no need to wrap it with a DataInputStream).

Here's how I'd write it:

Reader r = new InputStreamReader(System.in);
StringBuilder sb = new StringBuilder();
while(true) {
  int ch = r.read();
  if(ch < 0)
    break;
  sb.append((char) ch);
}

System.out.println(sb.toString());
痞味浪人 2024-11-10 13:37:32

available() 对于管道输入来说不可靠。它检查当前进程的输入缓冲区中是否有数据。它无法检查前面的(管道方式)进程是否要发送一些数据。

在您的情况下,阻止读取是一个可接受的解决方案:

public class Test {
    public static void main(String[] args) throws IOException {
        DataInputStream stdin = new DataInputStream(System.in);
        StringBuilder completeText = new StringBuilder(); 
        byte[] tempByte = new byte[1024];
        int len = 0;  
        while ((len = stdin.read(tempByte)) != -1) {
            completeText.append(new String(tempByte, 0, len));
        }
        System.out.println(completeText.toString());
    }
}

我还添加了 StringBuilder,因为这是连接字符串的“正确”java 方法。

available() is not reliable for piped input. It checks if there is data in the current process' input buffer. It has no way to check if preceding (pipe-wise) process is about to send some data.

In your case blocking read is an acceptable solution:

public class Test {
    public static void main(String[] args) throws IOException {
        DataInputStream stdin = new DataInputStream(System.in);
        StringBuilder completeText = new StringBuilder(); 
        byte[] tempByte = new byte[1024];
        int len = 0;  
        while ((len = stdin.read(tempByte)) != -1) {
            completeText.append(new String(tempByte, 0, len));
        }
        System.out.println(completeText.toString());
    }
}

I also added StringBuilder as this is the "correct" java way to concatenate strings.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文