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

Posted

技术标签:

【中文标题】如何从 java.lang.String 获取 java.io.InputStream?【英文标题】:How can I get a java.io.InputStream from a java.lang.String? 【发布时间】:2010-10-24 16:11:33 【问题描述】:

我有一个String,我想将它用作InputStream。在 Java 1.0 中,您可以使用 java.io.StringBufferInputStream,但一直是 @Deprecrated(有充分的理由——您不能指定字符集编码):

这个类没有正确转换 字符转换为字节。从 JDK 1.1 开始, 创建流的首选方式 来自字符串是通过StringReader 类。

您可以使用java.io.StringReader 创建java.io.Reader,但没有适配器可以使用Reader 并创建InputStream

我找到了一个 ancient bug 要求合适的替代品,但据我所知不存在这样的东西。

经常建议的解决方法是使用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 在内存中具体化为一个字节数组,并且违背了流的目的。在大多数情况下,这没什么大不了的,但我一直在寻找能够保留流意图的东西——尽可能少的数据(重新)在内存中实现。

【问题讨论】:

【参考方案1】:

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

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

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

【讨论】:

该答案提出的解决方案已被问题预期、考虑和拒绝。所以在我看来,这个答案应该被删除。 你可能是对的。我最初发表评论可能是因为它不是对 OP 问题的实际答案。 作为一个因为问题标题而来到这里的访客,我很高兴这个答案就在这里。所以:请不要删除这个答案。顶部的评论“这个答案正是 OP 不想要的。请阅读其他答案。”足够了。 从 java7 开始:new ByteArrayInputStream(str.getBytes(StandardCharsets.UTF_8))【参考方案2】:

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

【讨论】:

在这种情况下,您添加一个依赖项,该依赖项除了 `return new ByteArrayInputStream(input.getBytes());' 之外什么都不做这真的值得依赖吗?老实说,不 - 它不是。 是的,除此之外,这正是 op 不想使用的解决方法,因为他不想“将字符串具体化到内存中”,反对在系统:) 我们有没有将自定义对象转换为输入流源的库;类似 IOUtils.toInputStream(MyObject object)?【参考方案3】:

有一个来自 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://***.com/a/27909221/5658642

【讨论】:

【参考方案4】:

在我看来,最简单的方法是通过 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);
    

  


【讨论】:

【参考方案5】:

嗯,一种可能的方法是:

创建PipedOutputStream 通过管道将其发送到PipedInputStreamOutputStreamWriter 包裹在PipedOutputStream 周围(您可以在构造函数中指定编码) 等等,您写给OutputStreamWriter 的任何内容都可以从PipedInputStream 中读取!

当然,这似乎是一种相当老套的方式,但至少它是一种方式。

【讨论】:

有趣...当然,使用此解决方案,我相信您要么将整个字符串具体化在内存中,要么在阅读线程上遭受饥饿。仍然希望在某个地方有一个真正的实现。 您必须小心使用 Piped(Input|Output)Stream。根据文档:“...不建议尝试使用单个线程中的两个对象,因为它可能会使线程死锁...”java.sun.com/j2se/1.4.2/docs/api/java/io/PipedInputStream.html【参考方案6】:

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

【讨论】:

一次只做一个角色的成本很高。这就是为什么我们有像 InputStream 这样的“分块迭代器”,它允许我们一次读取一个缓冲区。 我同意汤姆的观点——你真的不想一次只做一个角色。 除非数据真的很小,而其他事情(例如网络延迟)需要更长的时间。然后没关系。 :)【参考方案7】:

您可以借助 org.hsqldb.lib 库。

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

【讨论】:

一般来说,问题如果包含对代码的用途的解释会更有用。【参考方案8】:

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

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++);
  
 ;

【讨论】:

以上是关于如何从 java.lang.String 获取 java.io.InputStream?的主要内容,如果未能解决你的问题,请参考以下文章

使用 Spark SQL 时无法将获取 B 转换为 java.lang.String

如何从 START_OBJECT 令牌中反序列化 java.lang.String 的实例

JOOQ r2dbc 获取数据:无法解码具有 OID 51968 的 java.lang.String 类型的值

常用类-String

18.3.2从Class上获取信息(构造器)

lava.lang.String数据转换为java.sql.Date