如何从java.lang.String获取java.io.InputStream?


95

我有一个String我想用作InputStream。在Java 1.0中,您可以使用java.io.StringBufferInputStream,但这已经@Deprecrated(有充分的理由-您无法指定字符集编码):

此类无法正确将字符转换为字节。从JDK 1.1开始,从字符串创建流的首选方法是通过StringReader 类。

您可以创建一个java.io.Readerjava.io.StringReader,但没有适配器采取Reader和创造InputStream

我发现一个古老的虫子正在寻找合适的替代品,但据我所知,这种虫子不存在。

通常建议的解决方法是java.lang.String.getBytes()用作java.io.ByteArrayInputStream

public InputStream createInputStream(String s, String charset)
    throws java.io.UnsupportedEncodingException {

    return new ByteArrayInputStream(s.getBytes(charset));
}

但这意味着将String内存中的整个实例化为字节数组,并破坏了流的目的。在大多数情况下,这没什么大不了的,但是我一直在寻找可以保留流意图的东西-尽可能少的数据在内存中重新实现。

Answers:


78

更新:这个答案正是OP所不想要的。请阅读其他答案。

对于那些我们不关心在内存中重新实现的数据的情况,请使用:

new ByteArrayInputStream(str.getBytes("UTF-8"))

3
该问题所提出的解决方案已被预期,考虑到并拒绝了。因此,我认为该答案应删除。
Mike Nakis

1
你可能是对的。我最初发表评论可能是因为这并不是对OP问题的实际答案。
Andres Riofrio

28
作为访问者由于问题标题而来这里,我很高兴这个答案在这里。因此:请不要删除此答案。顶部的备注“此答案正是OP所不需要的。请阅读其他答案。” 足够了。
Yaakov Belch

10
从Java7开始:new ByteArrayInputStream(str.getBytes(StandardCharsets.UTF_8))
缓慢

19

如果您不介意commons-io包的依赖性,则可以使用IOUtils.toInputStream(String text)方法。


11
在那种情况下,您添加了一个依赖项,除了“返回新的ByteArrayInputStream(input.getBytes());”之外什么也没有做。这真的值得依赖吗?老实说,不-不是。
whaefelinger 2012年

3
的确,除了操作人员不想使用的解决方法外,op并不想使用它,因为他不想将字符串具体化到内存中,而反对在系统中其他地方具体化字符串:)
Fotis Paraskevopoulos

我们是否有任何将自定义对象转换为输入流源的库?像IOUtils.toInputStream(MyObject object)之类的东西?
nawazish-stackoverflow

5

Apache Commons-IO提供了一个适配器,该适配器从Reader适应InputStream,名为ReaderInputStream

示例代码:

@Test
public void testReaderInputStream() throws IOException {
    InputStream inputStream = new ReaderInputStream(new StringReader("largeString"), StandardCharsets.UTF_8);
    Assert.assertEquals("largeString", IOUtils.toString(inputStream, StandardCharsets.UTF_8));
}

参考: https //stackoverflow.com/a/27909221/5658642


3

在我看来,最简单的方法是通过Writer推送数据:

public class StringEmitter {
  public static void main(String[] args) throws IOException {
    class DataHandler extends OutputStream {
      @Override
      public void write(final int b) throws IOException {
        write(new byte[] { (byte) b });
      }
      @Override
      public void write(byte[] b) throws IOException {
        write(b, 0, b.length);
      }
      @Override
      public void write(byte[] b, int off, int len)
          throws IOException {
        System.out.println("bytecount=" + len);
      }
    }

    StringBuilder sample = new StringBuilder();
    while (sample.length() < 100 * 1000) {
      sample.append("sample");
    }

    Writer writer = new OutputStreamWriter(
        new DataHandler(), "UTF-16");
    writer.write(sample.toString());
    writer.close();
  }
}

我使用的JVM实现以8K块的形式推送数据,但是通过减少一次写入的字符数并调用flush,可能会对缓冲区大小产生一些影响。


一种替代编写自己的CharsetEncoder包装器以使用Writer编码数据的方法,尽管这样做很痛苦。这应该是可靠(如果效率低下)的实现:

/** Inefficient string stream implementation */
public class StringInputStream extends InputStream {

  /* # of characters to buffer - must be >=2 to handle surrogate pairs */
  private static final int CHAR_CAP = 8;

  private final Queue<Byte> buffer = new LinkedList<Byte>();
  private final Writer encoder;
  private final String data;
  private int index;

  public StringInputStream(String sequence, Charset charset) {
    data = sequence;
    encoder = new OutputStreamWriter(
        new OutputStreamBuffer(), charset);
  }

  private int buffer() throws IOException {
    if (index >= data.length()) {
      return -1;
    }
    int rlen = index + CHAR_CAP;
    if (rlen > data.length()) {
      rlen = data.length();
    }
    for (; index < rlen; index++) {
      char ch = data.charAt(index);
      encoder.append(ch);
      // ensure data enters buffer
      encoder.flush();
    }
    if (index >= data.length()) {
      encoder.close();
    }
    return buffer.size();
  }

  @Override
  public int read() throws IOException {
    if (buffer.size() == 0) {
      int r = buffer();
      if (r == -1) {
        return -1;
      }
    }
    return 0xFF & buffer.remove();
  }

  private class OutputStreamBuffer extends OutputStream {

    @Override
    public void write(int i) throws IOException {
      byte b = (byte) i;
      buffer.add(b);
    }

  }

}

2

好吧,一种可能的方法是:

  • 创建一个 PipedOutputStream
  • 用管道将其 PipedInputStream
  • 一个包裹OutputStreamWriter周围的PipedOutputStream(可以指定在构造函数中的编码)
  • 等等,您写的任何内容都OutputStreamWriter可以从中读取PipedInputStream

当然,这似乎是一种很棘手的方法,但至少是一种方法。


1
有趣的是……当然,通过这种解决方案,我相信您要么在内存中实现了整个字符串,要么在读取线程上遭受了饥饿。仍然希望某个地方有一个真正的实现。
贾里德·奥伯豪斯

5
您必须小心Piped(Input | Output)Stream。根据文档:“ ...不建议尝试从单个线程使用两个对象,因为这可能会使线程死锁...” java.sun.com/j2se/1.4.2/docs/api/java/ io / PipedInputStream.html
Bryan Kyle,2009年

1

一种解决方案是自己动手,创建一个InputStream实现,该实现可能会根据需要使用java.nio.charset.CharsetEncoder将每个charchars 块编码为字节数组InputStream


1
一次只做一个角色就很昂贵。这就是为什么我们要像InputStream这样的“分块迭代器”,使我们一次读取一个缓冲区。
汤姆·霍汀-大头钉

我同意汤姆(Tom)的看法-您真的不想一次做一个角色。
Eddie

1
除非数据真的很小,否则其他事情(例如网络延迟)会花费更长的时间。那没关系。:)
Andres Riofrio'5

0

您可以使用org.hsqldb.lib库的帮助。

public StringInputStream(String paramString)
  {
    this.str = paramString;
    this.available = (paramString.length() * 2);
  }

1
通常,如果问题包含对代码意图的解释,则问题会更加有用。
彼得(Peter

-1

我知道这是一个老问题,但是今天我自己也遇到了同样的问题,这就是我的解决方案:

public static InputStream getStream(final CharSequence charSequence) {
 return new InputStream() {
  int index = 0;
  int length = charSequence.length();
  @Override public int read() throws IOException {
   return index>=length ? -1 : charSequence.charAt(index++);
  }
 };
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.