如何从 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
通过管道将其发送到PipedInputStream
将OutputStreamWriter
包裹在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
将每个char
或char
s 块编码为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 的实例