我有一个
String
,我想将其用作 InputStream
。在 Java 1.0 中,您可以使用 java.io.StringBufferInputStream
,但那已经是 @Deprecrated
(有充分的理由 - 您无法指定字符集编码):
该类无法正确转换 字符转换为字节。从 JDK 1.1 开始, 创建流的首选方式 来自字符串是通过
类。StringReader
java.io.Reader
创建 java.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
具体化为字节数组,并且违背了流的目的。在大多数情况下,这不是什么大问题,但我一直在寻找能够保留流意图的东西——尽可能少的数据在内存中(重新)具体化。
更新:这个答案正是OP所不想要的。请阅读其他答案。
对于那些我们不关心数据在内存中重新物化的情况,请使用:
new ByteArrayInputStream(str.getBytes("UTF-8"))
如果您不介意依赖于 commons-io 包,那么您可以使用 IOUtils.toInputStream(String text) 方法。
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));
}
在我看来,最简单的方法是通过 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 块的形式推送数据,但您可以通过减少一次写入的字符数并调用刷新来对缓冲区大小产生一些影响。
编写自己的 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);
}
}
}
嗯,一种可能的方法是:
PipedOutputStream
PipedInputStream
OutputStreamWriter
周围包裹一个
PipedOutputStream
(您可以在构造函数中指定编码)OutputStreamWriter
的任何内容都可以从 PipedInputStream
中读取!当然,这似乎是一种相当黑客的方法,但至少这是一种方法。
一个解决方案是自己动手,创建一个
InputStream
实现,可能会根据需要使用 java.nio.charset.CharsetEncoder
将每个 char
或 char
块编码为 InputStream
的字节数组。
您可以借助 org.hsqldb.lib 库的帮助。
public StringInputStream(String paramString)
{
this.str = paramString;
this.available = (paramString.length() * 2);
}
如果项目中有 Guava 库,则可以通过以下方式使用内部 ReaderInputStream / CharSequenceReader:
public InputStream asInputStream(CharSequence seq, Charset charset) throws IOException {
return CharSource.wrap(s).asByteSource(charset).openStream();
}
我知道这是一个老问题,但我今天也遇到了同样的问题,这是我的解决方案:
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++);
}
};
}