2009-05-08 7 views
83

Ich habe eine String, die ich als InputStream verwenden möchte. In Java 1.0, könnten Sie java.io.StringBufferInputStream, verwenden aber das hat @Deprecrated gewesen (mit gutem Grund - nicht den Zeichensatz-Codierung angeben):Wie kann ich einen java.io.InputStream von einem java.lang.String bekommen?

Diese Klasse Zeichen in Bytes nicht richtig konvertieren. Ab JDK 1.1, ist die bevorzugte Methode zum Erstellen eines Streams aus einer Zeichenfolge über die StringReader Klasse.

Sie können eine java.io.Reader mit java.io.StringReader erstellen, aber es gibt keine Adapter eine Reader zu nehmen und eine InputStream zu erstellen.

Ich fand eine ancient bug Frage nach einem geeigneten Ersatz, aber so etwas existiert nicht - soweit ich das beurteilen kann.

Die oft vorgeschlagene Abhilfe ist java.lang.String.getBytes() als Eingabe für java.io.ByteArrayInputStream zu verwenden:

public InputStream createInputStream(String s, String charset) 
    throws java.io.UnsupportedEncodingException { 

    return new ByteArrayInputStream(s.getBytes(charset)); 
} 

aber das heißt, die gesamten String im Speicher als eine Reihe von Bytes zu materialisieren, und den Zweck des Stroms. In den meisten Fällen ist das keine große Sache, aber ich suchte nach etwas, das die Absicht eines Streams bewahren würde - dass so wenig Daten wie möglich im Speicher (re) materialisiert werden.

+0

@djb Nur so. –

Antwort

2

Nun, eine Möglichkeit ist:

  • erstellen PipedOutputStream
  • Rohr es zu einem PipedInputStream
  • Wrap ein OutputStreamWriter um die PipedOutputStream (Sie die Codierung im Konstruktor angeben)
  • Et voil & aacute ;, alles, was Sie auf die OutputStreamWriter schreiben, können Sie aus der PipedInputStream lesen!

Natürlich scheint dies eine ziemlich hackische Art, es zu tun, aber zumindest ist es ein Weg.

+1

Interessant ... natürlich, mit dieser Lösung glaube ich, dass Sie entweder die ganze Zeichenfolge im Gedächtnis materialisieren würden, oder am Lesethread leiden würden. Ich hoffe immer noch, dass es irgendwo eine echte Implementierung gibt. –

+4

Sie müssen mit Piped (Input | Output) Stream vorsichtig sein. Laut der Dokumentation: "... Es wird nicht empfohlen, beide Objekte aus einem einzigen Thread zu verwenden, da dies den Thread blockieren kann ..." http://java.sun.com/j2se/1.4.2/docs/ api/java/io/PipedInputStream.html –

2

Eine Lösung ist selber zu rollen, eine InputStream Implementierung zu schaffen, die wahrscheinlich java.nio.charset.CharsetEncoder verwenden würde jede char oder Brocken char s auf ein Array von Bytes für die InputStream wie nötig zu kodieren.

+1

Es ist teuer, ein Zeichen gleichzeitig zu schreiben. Deshalb haben wir "chunked iterators" wie InputStream, die es uns erlauben, einen Puffer zu einem Zeitpunkt zu lesen. –

+0

Ich stimme Tom zu - du ** willst wirklich nicht diesen einen Charakter auf einmal machen. – Eddie

+1

Es sei denn, die Daten sind wirklich klein, und andere Dinge (Netzwerklatenz, zum Beispiel) dauern länger. Dann ist es egal. :) –

3

Meiner Meinung nach, ist der einfachste Weg, dies zu tun ist, indem die Daten durch einen Writer drängen:

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 ich in 8K Stücke geschoben Daten durch bin mit, aber man könnte einige beeinflussen auf die Puffergröße, indem die Anzahl der gleichzeitig geschriebenen Zeichen reduziert und flush aufgerufen wird.


Eine Alternative zum eigenen CharsetEncoder Wrapper Schreiben ein Writer zu verwenden, um die Daten zu codieren, obwohl es etwas von einem Schmerz richtig zu tun ist.Dies sollte eine zuverlässige (wenn ineffizient) 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); 
    } 

    } 

} 
+3

Diese "einfachste" Lösung benötigt viel Code. – kurtzbot

16

Wenn Sie nicht eine Abhängigkeit von dem commons-io Paket dagegen hat, dann könnten Sie die IOUtils.toInputStream(String text) Methode verwenden.

+9

In diesem Fall fügen Sie eine Abhängigkeit hinzu, die nichts anderes als 'return new ByteArrayInputStream (input.getBytes()); ' Ist das wirklich eine Abhängigkeit wert? Um ehrlich zu sein, nein - ist es nicht. – whaefelinger

+1

True, außerdem ist es genau die Workaround, die das OP nicht verwenden möchte, weil er nicht die Zeichenfolge in den Speicher "materialisieren" möchte, die an anderer Stelle im System materialisiert wird :) –

+0

Haben wir jede Bibliothek, die das benutzerdefinierte Objekt in die Quelle des Eingabestreams konvertiert; etwas wie IOUtils.toInputStream (MyObject-Objekt)? –

72

Update: Diese Antwort ist genau das, was das OP nicht will. Bitte lesen Sie die anderen Antworten.

Für die Fälle, wenn wir über die Daten nicht kümmern im Speicher wird wieder materialisiert, bitte:

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

Die Lösung, die durch diese Antwort vorgeschlagen wird, wurde von der Frage vorweggenommen, betrachtet und abgelehnt. Also meiner Meinung nach sollte diese Antwort gelöscht werden. –

+1

Sie könnten Recht haben. Ich habe es ursprünglich kommentiert, wahrscheinlich weil es keine wirkliche Antwort auf die Frage von OP war. –

+23

Als Besucher, der wegen des Fragetitels hierher kommt, bin ich froh, dass diese Antwort hier ist. Also: Bitte lösche diese Antwort nicht. Die Bemerkung oben "Diese Antwort ist genau das, was das OP nicht will. Bitte lesen Sie die anderen Antworten." ist ausreichend. –

0

Ich weiß, dass dies eine alte Frage, aber ich hatte das gleiche Problem selbst heute, 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++); 
    } 
}; 
} 
+6

Dies funktioniert nicht für Nicht-ASCII. – slow

3

Es ist ein Adapter von Apache Commons-IO, die von Reader Input paßt, die ReaderInputStream gestattet.

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

Referenz: https://stackoverflow.com/a/27909221/5658642

0

Sie Hilfe org.hsqldb.lib Bibliothek nehmen.

public StringInputStream(String paramString) 
    { 
    this.str = paramString; 
    this.available = (paramString.length() * 2); 
    } 
+1

Im Allgemeinen sind Fragen viel nützlicher, wenn sie eine Erklärung enthalten, was der Code tun soll. – Peter