Ich habe eine String
, die ich als InputStream
verwenden möchte. In Java 1.0 können Sie Java.io.StringBufferInputStream
verwenden, dies war jedoch @Deprecrated
(aus gutem Grund - Sie können die Zeichensatzkodierung nicht angeben):
Diese Klasse konvertiert nicht korrekt Zeichen in Bytes. Stand JDK 1.1, Die bevorzugte Methode zum Erstellen eines Streams von einer Zeichenfolge erfolgt über
StringReader
Klasse.
Sie können ein Java.io.Reader
mit Java.io.StringReader
erstellen, aber es gibt keine Adapter, die ein Reader
und ein InputStream
erstellen.
Ich habe ein uralter Bug gefunden, das nach einem geeigneten Ersatz fragt, aber so etwas existiert nicht - soweit ich das beurteilen kann.
Die häufig empfohlene Problemumgehung besteht in der Verwendung von Java.lang.String.getBytes()
als Eingabe für Java.io.ByteArrayInputStream
:
public InputStream createInputStream(String s, String charset)
throws Java.io.UnsupportedEncodingException {
return new ByteArrayInputStream(s.getBytes(charset));
}
dies bedeutet jedoch, dass das gesamte String
im Speicher als Byte-Array verwirklicht wird, und der Zweck eines Streams besiegt wird. In den meisten Fällen ist dies keine große Sache, aber ich suchte nach etwas, das die Absicht eines Streams aufrechterhalten würde - dass so wenig Daten wie möglich im Speicher (wieder) materialisiert werden.
Update: Diese Antwort ist genau das, was das OP nicht will. Bitte lesen Sie die anderen Antworten.
Für die Fälle, in denen es uns nicht darum geht, dass die Daten im Speicher erneut materialisiert werden, verwenden Sie bitte:
new ByteArrayInputStream(str.getBytes("UTF-8"))
Wenn Sie nichts an einer Abhängigkeit vom Paket commons-io interessiert, können Sie die Methode IOUtils.toInputStream (String text) verwenden.
Es gibt einen Adapter von Apache Commons-IO, der sich von Reader an InputStream anpasst, der ReaderInputStream heißt.
Beispielcode:
@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));
}
Meiner Meinung nach ist dies der einfachste Weg, die Daten durch einen Writer zu pushen:
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();
}
}
Die JVM-Implementierung, die ich in 8-KByte-Paketen verwende, verwende Daten, aber Sie könnten die Puffergröße beeinflussen, indem Sie die Anzahl der gleichzeitig geschriebenen Zeichen und den Aufruf von Flush verringern.
Eine Alternative zum Schreiben Ihres eigenen CharsetEncoder-Wrappers, um einen Writer zum Verschlüsseln der Daten zu verwenden, obwohl es ziemlich schmerzhaft ist, dies richtig zu tun. Dies sollte eine zuverlässige (wenn ineffiziente) Implementierung sein:
/** 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);
}
}
}
Nun, ein möglicher Weg ist:
PipedOutputStream
PipedInputStream
OutputStreamWriter
um das PipedOutputStream
(Sie können die Kodierung im Konstruktor angeben)OutputStreamWriter
schreiben, kann von der PipedInputStream
gelesen werden!Natürlich scheint dies eine ziemlich hackhafte Art zu sein, aber es ist zumindest ein Weg.
Eine Lösung besteht darin, Ihre eigene Rolle zu erstellen, indem Sie eine InputStream
-Implementierung erstellen, die wahrscheinlich Java.nio.charset.CharsetEncoder
zum Kodieren der einzelnen char
-Elemente oder der einzelnen char
-Blöcke in ein Array von Bytes für die InputStream
-Instanz verwendet.
Ich weiß, das ist eine alte Frage, aber ich hatte heute selbst das gleiche Problem, und dies war meine Lösung:
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++);
}
};
}
Sie können sich an die Bibliothek org.hsqldb.lib wenden.
public StringInputStream(String paramString)
{
this.str = paramString;
this.available = (paramString.length() * 2);
}