能否从方法中的java InputStream连续读取数据?

huangapple go评论76阅读模式
英文:

Can a java InputStream continuously read data from a method?

问题

我有一段代码

...
InputStream inputStream = new BufferedInputStream(new ByteArrayInputStream("test".getBytes()));
...

而这一行使字符串 "test" 成为 InputStream 的输入,但这是一个静态 InputStream。
是否有一种方式可以使这个 InputStream 动态化,而不使用 Scanner、System.in 或用户外部输入?

我需要的是像这样的东西

...
InputStream inputStream = new BufferedInputStream(new 
ByteArrayInputStream(generateContinuousDynamicString().getBytes()));
// 所以,基本上输入流会被阻塞,直到 generateContinuousDynamicString() 返回结果?
...

我尝试过像这样的东西

private static byte[] generateContinuousDynamicString(String s) {
    String t = "";
    // 这里意识到
    // 输入流的来源
    // 不能在飞行中动态生成
    // 它只能从已有的
    // (完全生成并可用的)
    // 资源中读取。我是对的吗?
    // 否则,如何调整此方法,
    // 以便输入流持续拥有一个新的
    // 字符串可供读取?
    for (int i = 0; i < 1000; i++){
        t += "<str>"+s+i+"</str>";
    }
    return ("<test>"+t+"</test>").getBytes();
}

所以,如果我们有

...
InputStream inputStream = new BufferedInputStream(readFromADatabaseStream());
...

这也不是动态输入流,因为资源已经在数据库中。

英文:

I have a piece of code

...
InputStream inputStream = new BufferedInputStream(new ByteArrayInputStream(&quot;test&quot;.getBytes()));
...

and this line makes string "test" an input for an InputStream, however this is a static InputStream.
is there any way without a Scanner, System.in or user external input to make this InputStream dynamic

what I need is something like this

...
InputStream inputStream = new BufferedInputStream(new 
ByteArrayInputStream(generateContinuousDynamicString().getBytes()));
// So, basically input stream will be blocked until generateContinuousDynamicString()
// returns a result?
...

I've tried something like this

private static byte[] generateContinuousDynamicString(String s) {
    String t = &quot;&quot;;
    // here comes the realization
    // that the source for an input stream 
    // cannot be generated dynamically on the 
    // fly it only can be read from already 
    // existing (fully generated and available 
    // resource). Am I right? Otherwise how 
    // can I adjust this method in such a way that
    // input stream would continuously have a new
    // string to read from? 
    for (int i = 0; i &lt; 1000; i++){
        t += &quot;&lt;str&gt;&quot;+s+i+&quot;&lt;/str&gt;&quot;;
    }
    return (&quot;&lt;test&gt;&quot;+t+&quot;&lt;/test&gt;&quot;).getBytes();
}

So, if we have

...
InputStream inputStream = new BufferedInputStream(readFromADatabaseStream());
...

This is also not dynamic input stream as a resource is already in a database.

答案1

得分: 2

你想要一个管道。具体来说,你想要以下这些类的其中一对:

你的问题要求一个InputStream,但因为你处理的是文本,你可能应该使用Reader,它用于字符。特别要注意的是,在Windows系统和非Windows系统上,对于带有非ASCII字符的任何字符串,getBytes()会返回不同的值。使用Reader和Writer将消除对此的担忧。

无论哪种方式,方法是相同的:先创建可读端的管道,然后在另一个线程中创建并提供可写端的管道。

使用PipedReader和PipedWriter:

PipedReader pipedReader = new PipedReader();
Reader reader = new BufferedReader(pipedReader);

ExecutorService executor = Executors.newSingleThreadExecutor();
Future<?> pipeFeeder = executor.submit(
    () -> generateContinuousDynamicString(pipedReader));

// ...

private Void generateContinuousDynamicString(PipedReader pipedReader)
throws IOException {

    try (Writer writer = new PipedWriter(pipedReader)) {
        writer.write("<test>");

        for (int i = 0; i < 1000; i++) {
            writer.write("<str>" + i + "</str>");
        }

        writer.write("</test>");
    }

    return null;
}

使用PipedInputStream和PipedOutputStream:

PipedInputStream pipedInputStream = new PipedInputStream();
InputStream inputStream = new BufferedInputStream(pipedInputStream);

ExecutorService executor = Executors.newSingleThreadExecutor();
Future<?> pipeFeeder = executor.submit(
    () -> generateContinuousDynamicString(pipedInputStream));

// ...

private Void generateContinuousDynamicString(PipedInputStream pipedInputStream)
throws IOException {

    Charset charset = StandardCharsets.UTF_8;

    try (Writer writer = new OutputStreamWriter(
            new PipedOutputStream(pipedInputStream),
            StandardCharsets.UTF_8)) {

        writer.write("<test>");

        for (int i = 0; i < 1000; i++) {
            writer.write("<str>" + i + "</str>");
        }

        writer.write("</test>");
    }

    return null;
}
英文:

You want a pipe. Specifically, you want one of the following pairs of classes:

Your question asks for an InputStream, but since you’re dealing with text, you probably should use a Reader, which is intended for characters. In particular, note that getBytes() will return different values on Windows systems compared to non-Windows systems, for any String with non-ASCII characters. Using a Reader and Writer will remove the need to worry about that.

Either way, the approach is the same: create the readable end of the pipe, then create and feed the writable end of the pipe in another thread.

Using a PipedReader and PipedWriter:

PipedReader pipedReader = new PipedReader();
Reader reader = new BufferedReader(pipedReader);

ExecutorService executor = Executors.newSingleThreadExecutor();
Future&lt;?&gt; pipeFeeder = executor.submit(
    () -&gt; generateContinuousDynamicString(pipedReader));

// ...

private Void generateContinuousDynamicString(PipedReader pipedReader)
throws IOException {

    try (Writer writer = new PipedWriter(pipedReader)) {
        writer.write(&quot;&lt;test&gt;&quot;);

        for (int i = 0; i &lt; 1000; i++) {
            writer.write(&quot;&lt;str&gt;&quot; + i + &quot;&lt;/str&gt;&quot;);
        }

        writer.write(&quot;&lt;/test&gt;&quot;);
    }

    return null;
}

Using a PipedInputStream and PipedOutputStream:

PipedInputStream pipedInputStream = new PipedInputStream();
InputStream inputStream = new BufferedInputStream(pipedInputStream);

ExecutorService executor = Executors.newSingleThreadExecutor();
Future&lt;?&gt; pipeFeeder = executor.submit(
    () -&gt; generateContinuousDynamicString(pipedInputStream));

// ...

private Void generateContinuousDynamicString(PipedInputStream pipedInputStream)
throws IOException {

    Charset charset = StandardCharsets.UTF_8;

    try (Writer writer = new OutputStreamWriter(
            new PipedOutputStream(pipedInputStream),
            StandardCharsets.UTF_8)) {

        writer.write(&quot;&lt;test&gt;&quot;);

        for (int i = 0; i &lt; 1000; i++) {
            writer.write(&quot;&lt;str&gt;&quot; + i + &quot;&lt;/str&gt;&quot;);
        }

        writer.write(&quot;&lt;/test&gt;&quot;);
    }

    return null;
}

答案2

得分: 0

当然。但是你有一个小问题:生成无尽动态数据流的任何代码都不能仅仅位于“返回输入流”的方法中,就像你所意识到的那样。

你有两个主要选项:

线程

相反,你可以启动一个线程,不断生成数据。需要注意的是,它所“生成”的任何内容都需要被缓存;举个例子,如果你想动态生成一个仅提供无尽的0字节的输入流,这种方法__不太合适__。但如果数据来自于一个USB连接的Arduino,它不时地发送连接的温度传感器的信息,那么这是一个不错的选择。需要注意的是,你需要让线程将接收到的数据存储在某个地方,然后有一个输入流从你制作的这些数据队列中“拉取”。要创建一个从队列中读取的输入流,请查看下一节。由于这涉及线程,可以使用java.util.concurrent中的一些东西,比如ArrayBlockingQueue - 这样做的双重好处是你也不会得到无限的缓冲区(如果缓冲区已满,则放入缓冲区的操作将被阻塞)。

子类化

你还可以将能够生成新值的代码放入一个信封中,可以传递这个信封。你想要创建一些代码,但是不运行它 - 你希望稍后在将输入流传递给处理它的东西调用.read()时再运行它。

一个简单的方法是,扩展InputStream - 然后实现你自己的零值方法。看起来是这样的:


class InfiniteZeroesInputStream extends InputStream {
    public int read() {
        return 0;
    }
}

就是这么简单。考虑到:

try (InputStream in = new InfiniteZeroesInputStream()) {
    in.read(); // 返回 0.. 并且始终如此。
    byte[] b = new byte[65536];
    in.read(b); // 用零填充整个数组。
}
英文:

Sure. But you have a bit of an issue: Whatever code is generating the endless stream of dynamic data cannot just be in the method that 'returns the inputstream' just by itself, that's what your realisation is about.

You have two major options:

Threads

Instead, you could fire off a thread which is continually generating data. Note that whatever it 'generates' needs to be cached; this is not a good fit if, say, you want to dynamically generate an inputstream that just serves up an endless amount of 0 bytes, for example. It's a good fit if the data is coming from, say, a USB connected arduino that from time to time sends information about a temperature sensor that it's connected to. Note that you need the thread to store the data it receives someplace, and then have an inputstream that will 'pull' from this queue of data you're making. To make an inputstream that pulls from a queue, see the next section. As this will involve threads, use something from java.util.concurrent, such as ArrayBlockingQueue - this has the double benefit that you won't get infinite buffers, either (the act of putting something in the buffer will block if the buffer is full).

subclassing

What you can also do is take the code that can generate new values, but, put it in an envelope - a thing you can pass around. You want to make some code, but not run it - you want to run that later, when the thing you hand the inputstream to, calls .read().

One easy way to do that, is to extend InputStream - and then implement your own zero method. Looks something like this:


class InfiniteZeroesInputStream extends InputStream {
    public int read() {
        return 0;
    }
}

It's that simple. Given:

try (InputStream in = new InfiniteZeroesInputStream()) {
    in.read(); // returns 0.. and will always do so.
    byte[] b = new byte[65536];
    in.read(b); // fills the whole array with zeroes.
}

huangapple
  • 本文由 发表于 2020年9月27日 08:20:53
  • 转载请务必保留本文链接:https://go.coder-hub.com/64083687.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定