Reputation: 14678
I have a String
that I want to use as an InputStream
. In Java 1.0, you could use java.io.StringBufferInputStream
, but that has been @Deprecrated
(with good reason--you cannot specify the character set encoding):
This class does not properly convert characters into bytes. As of JDK 1.1, the preferred way to create a stream from a string is via the
StringReader
class.
You can create a java.io.Reader
with java.io.StringReader
, but there are no adapters to take a Reader
and create an InputStream
.
I found an ancient bug asking for a suitable replacement, but no such thing exists--as far as I can tell.
The oft-suggested workaround is to use java.lang.String.getBytes()
as input to java.io.ByteArrayInputStream
:
public InputStream createInputStream(String s, String charset)
throws java.io.UnsupportedEncodingException {
return new ByteArrayInputStream(s.getBytes(charset));
}
but that means materializing the entire String
in memory as an array of bytes, and defeats the purpose of a stream. In most cases this is not a big deal, but I was looking for something that would preserve the intent of a stream--that as little of the data as possible is (re)materialized in memory.
Upvotes: 96
Views: 85824
Reputation: 603
I am not aware of any possibility with pure JDK means. The StringBufferInputStream is deprecated because it does not convert characters into bytes properly.
If Guava library is available in the project, the internal ReaderInputStream can be used via:
public InputStream asInputStream(CharSequence chars, Charset charset) throws IOException {
return CharSource.wrap(chars).asByteSource(charset).openStream();
}
Under the hood the CharSequence/String is wrapped into a Reader which in turn is wrapped into the InputStream mentioned in the beginning. This allows the conversion via streaming.
Upvotes: 0
Reputation: 1
You can take help of org.hsqldb.lib library.
public StringInputStream(String paramString)
{
this.str = paramString;
this.available = (paramString.length() * 2);
}
Upvotes: -1
Reputation: 2032
There is an adapter from Apache Commons-IO which adapts from Reader to InputStream, which is named ReaderInputStream.
Example code:
@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));
}
Reference: https://stackoverflow.com/a/27909221/5658642
Upvotes: 6
Reputation: 1207
I know this is an old question but I had the same problem myself today, and this was my solution:
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++);
}
};
}
Upvotes: -2
Reputation: 10377
Update: This answer is precisely what the OP doesn't want. Please read the other answers.
For those cases when we don't care about the data being re-materialized in memory, please use:
new ByteArrayInputStream(str.getBytes("UTF-8"))
Upvotes: 78
Reputation: 1011
If you don't mind a dependency on the commons-io package, then you could use the IOUtils.toInputStream(String text) method.
Upvotes: 19
Reputation: 108969
To my mind, the easiest way to do this is by pushing the data through a 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();
}
}
The JVM implementation I'm using pushed data through in 8K chunks, but you could have some affect on the buffer size by reducing the number of characters written at one time and calling flush.
An alternative to writing your own CharsetEncoder wrapper to use a Writer to encode the data, though it is something of a pain to do right. This should be a reliable (if inefficient) implementation:
/** 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);
}
}
}
Upvotes: 3
Reputation: 14678
A solution is to roll your own, creating an InputStream
implementation that likely would use java.nio.charset.CharsetEncoder
to encode each char
or chunk of char
s to an array of bytes for the InputStream
as necessary.
Upvotes: 1
Reputation: 192055
Well, one possible way is to:
PipedOutputStream
PipedInputStream
OutputStreamWriter
around the PipedOutputStream
(you can specify the encoding in the constructor)OutputStreamWriter
can be read from the PipedInputStream
!Of course, this seems like a rather hackish way to do it, but at least it is a way.
Upvotes: 2